;*************************************************
;*						 *
;* BIGBOARD MONITOR ROM, NON-RELOCATABLE VERSION *
;*		61 K version			 *
;*  Original outline: Russell Smith, 2-Aug-80	 *
;*  Rewritten by Trevor Marshall, Feb-Mar 81	 *
;*	for self-contained WAIT-AID BIOS	 *
;*						 *
;*************************************************
;
;
TRUE	EQU	-1
FALSE	EQU	0
;
IS5MHZ	EQU	TRUE
ROM:	EQU	FALSE
SIO.POLLED EQU	TRUE
SIO.INTERRUPT EQU NOT SIO.POLLED
PARALLEL EQU	TRUE
SERIAL 	EQU	FALSE
CACHE	EQU	TRUE
;
ROMLOC:	EQU	0F000H		;START OF 2K ROM
;
	ORG	ROMLOC-10H	; at 0 on reset,100 if disk
;
	DI

	IF	NOT ROM
	LD	HL,110H	;Where the code will be loaded
	ENDIF	;NOT ROM

	IF	ROM
	LD	HL,10H ;Where code is in ROM
	ENDIF	;ROM

	LD	DE,ROMLOC	;Where we want it put
	LD	BC,0FF0H ;remaining Length of this ROM
			;and the next
	LDIR
	JP	INIT	;Initialize all parameters
	NOP		;To use up 16 bytes
;
;************************************************
;*						*
;*   COLD START INITIALIZATION ROUTINE FOR	*
;*   CONFIGURING THE SYSTEM AFTER A POWER-ON	*
;*    OR PUSHBUTTON RESET.			*
;*				18-Oct-80	*
;*						*
;************************************************
;
;
; CP/M Equates
CPMBASE	EQU	0D800H	;CP/M base address
CCP	EQU	CPMBASE ;CONSOLE COMMAND PROCESSOR
BDOS	EQU	806H+CPMBASE ;OPERATING SYSTEM ENTRY POINT
CBIOS	EQU	1600H+CPMBASE ;BASE OF CUSTOM BIOS

;
;	-- MONITOR ENTRY POINT TABLE --
;
; We need to catch all the external calls to the BIOS
;   so that we can restore registers upon error
;   and handle stack imbalance from error control
; BIOS FUNCTION CALLING TABLE
COLD:	JP	COLDBOOT ;COLD
	JP	WARMBOOT ;WARM
	JP  	CPMST 	;CONST
	JP  	CPMIN 	;CONIN
	JP  	CPMOUT	;CONOUT
	JP	LIST	;LIST
	JP  	CPMOUT	;PUNCH
	JP  	KBDIN 	;READER
	CALL	BIOSGO	;HOME
	CALL	BIOSGO	;SELDSK
	CALL	BIOSGO	;SETTRK
	JP	SETSEC	;SETSEC
	JP	SETDMA	;SETDMA
	CALL	BIOSGO	;READ
	CALL	BIOSGO	;WRITE
	JP  	LSTST 	;LSTST
	JP  	SECTRAN	;SECTRAN
; A few extra easily accessible subroutines
	JP	INIT

	IF	ROM
	JP	PROMPT
	ENDIF	;ROM

;Now we need a jump table for the routines
BIOSTBL: JP	COLDBOOT
	JP	WARMBOOT
	JP	CPMST
	JP	CPMIN
	JP	CPMOUT
	JP	LIST
	JP	CPMOUT	;Punch
	JP	KBDIN	;Reader
	JP	HOME
	JP	SELECT
	JP	SEEK
	JP	SETSEC
	JP	SETDMA
	JP	READ
	JP	WRITE
;
BIOSGO:	LD	(HLSAV),HL	;Save all registers
	LD	(DESAV),DE
	LD	(BCSAV),BC
	POP	HL	;Get calling address in HL
	LD	A,L	;Arithmetic with L only is OK
	SUB	A,3	;Point at start of CALL
	LD	L,A	;with HL
	LD	(OFFSET),A ;Save function number
	LD	(SPSAV),SP ; Save Stack Pointer at entry
	LD	DE,BIOSTBL-COLD ;Add the table length to first table
	ADD	HL,DE		;To point to corresponding entry
	LD	(ROUTINE.ADDRESS),HL ;Save it for error rentry
	LD	DE,ERROR.HANDLER ;Fake a return to Error Handler
	PUSH	DE
	LD	DE,(DESAV) ;REstore DE
; Trying JP(IX) BLEW DEBUG
	JP	(HL)	;Go to BIOS routine
	

ERROR.HANDLER: PUSH	HL ;Save return status, acc.
;Is error status zero?
	PUSH	AF
	OR	A
	JR	Z,RET9	;Return if no error indicated
	CALL	PNEXT	;Output an error message
	DB	0DH,0AH,7,'***** Disk I/O error, trk/sec ',EOT
	LD	A,(TRACK)
	CALL	PUT2HX
	LD	A,'/'
	CALL	CONOUT
	LD	A,(SECTOR)
	CALL	PUT2HX
	CALL	PNEXT
	DB	', FDC status ',EOT
	POP	AF
	PUSH	AF	;Get status
	CALL	PUT2HS	;Put it in HEX
	CALL	CONIN
	AND	5FH	;LC -> UC
	CP	'R'
	JR	Z,TRYAGAIN
	CP	'I'
	JR	Z,IGNORE
;Else drop through to RET9 and return error status
	CALL	PNEXT
	DB	'continue',EOT
RET9:
;If functions return values in HL, trap the HL restore
	POP	AF
	POP	HL	;get HL
	PUSH	AF	;acc destroyed below
	LD	A,(OFFSET)
	CP	1BH		;SELDSK
	JR	EQ,RET10
	LD	HL,(HLSAV)
RET10:	LD	DE,(DESAV)
	LD	BC,(BCSAV)
	POP	AF	;retrieve accumulator
;	LD	SP,(SPSAV)
	RET

TRYAGAIN: CALL	PNEXT
	DB	'retry',EOT
	LD	A,(TRACK)	;save TRK #
	LD	HL,PARAM1	;in scratch
	LD	(HL),A
	INC	HL
	LD	A,(SECTOR)
	LD	(HL),A
	CALL	HOME	;Recalibrate the drive
;Disregard any errors here, easier to handle them upon return
	LD	HL,PARAM1
	LD	C,(HL)
	CALL	SEEK
	LD	A,(PARAM1+1)	;Restore original sector
	LD	(SECTOR),A	;Effectively CALL SETSEC
	LD	BC,(BCSAV)	;Restore relevant calling data
	LD	HL,(ROUTINE.ADDRESS)
	LD	SP,(SPSAV)	;where we began
	LD	DE,ERROR.HANDLER
	PUSH	DE	;Fake a return to the handler
	JP	(HL)
;
IGNORE:	CALL	PNEXT
	DB	'ignore',EOT
;
	POP	AF
	XOR	A	;make error status = 0 and return
	PUSH	AF
	JR	RET9
;
; Indirect call to subroutines, modifying code
CPMOUT:	LD	A,C
CONOUT:	JP	CRTOUT	;Modified by SWITCH routines
CPMIN:
CONIN:	JP	KBDIN
CPMST:
CONST:	JP	KBDST
;
LSTST:
	IF	NOT ROM

	IN	A,PIOBDAT	;Is printer busy?
	CPL
	AND	2	;if LO, busy
	RET	Z	;if so ret Zero

	ENDIF	;NOT ROM

	LD	A,0FFH
	RET
;
LIST:
	IF	NOT ROM

	PUSH	AF
LP1Z:	CALL	LSTST
	JR	Z,LP1Z
	POP	AF
	OUT	PIOADAT,A
	IN	A,PIOBDAT	;Strobe port B bit 7
	RES	7,A
	OUT	PIOBDAT,A
	SET	7,A
	OUT	PIOBDAT,A

	ENDIF	;NOT ROM

	RET
;
;The CCP jumps to coldboot if there is an boot error
COLDBOOT: XOR	A
	LD	(0003H),A ;RESET IOBYTE TO ZEROS
;CCP uses location 4 as a store for user & drive
	LD	(4),A ;Select User 0 & A: on COLDboot
	JR	GOCPM
;
;
WARMBOOT: LD	SP,STACK
; We will select in the CCP ROM by writing
;  a 1 to port 1C bit 7
	IN	A,(BITDAT)
	SET	7,A
	OUT	(BITDAT),A
; the PROM is in socket 3, 1000H up
	LD	BC,800H ;move prom contents
	LD	DE,CCP
	LD	HL,1000H
	LDIR
;
	IN	A,(BITDAT)	;Deselect PROM
	RES	7,A
	OUT	(BITDAT),A
;
;We must zero the bottom bytes of BDOS
;So that the serial numbers match
	LD	B,6
	XOR	A
	LD	HL,BDOS-6	;or CPMBASE + 800H
LPP3:	LD	(HL),A
	INC	HL
	DJNZ	LPP3
;
;Zero all the allocation vector tables
GOCPM:	LD	BC,VECTORS.LENGTH-1
	LD	DE,ALLOCATION.VECTORS+1
	LD	HL,ALLOCATION.VECTORS
	LD	(HL),A
	LDIR
; Zero our BIOS logged disks
	CALL	LOGOFF
;
	LD	A,0C3H	;STORE JUMP VECTORS IN RAM
	LD	(00H),A
	LD	HL,CBIOS+3 ;JUMP TO CBIOS WARM BOOT AT 00H
	LD	(01H),HL
	LD	(05H),A
	LD	HL,BDOS	;JUMP TO BDOS GOES AT 05H
	LD	(06H),HL

	IF	ROM
	LD	(38H),A
	LD	HL,BKPT  ;JUMP TO Breakpoint GOES AT 38H
	LD	(39H),HL
	ENDIF	;ROM

	LD	HL,FILTER ;Jump to filter replaces that in BDOS
	LD	(BDOS+1),HL

	LD	BC,0080H
	CALL	SETDMA	 ;MAKE DISK BUFFER=0080H

;The CCP takes in C   | User# | Drive# |

	LD	A,(4)	;Get current user/drive value
	LD	C,A	;into C for CCP
;
	JP	CCP
;
;
LOGOFF: ;Log off both disks
	LD	HL,UNIT	;Point to TRKTAB-1
	LD	A,-1	;'CLEAR' it
	LD	(HL),A
	INC	HL
	LD	(HL),A
	INC	HL
	LD	(HL),A	 ;=== 2 Disk system
	RET
;
;********************************************************
;* Entry point at initialization			*
;********************************************************
;
;DO A SHORT POST-RESET TIME DELAY. ALSO INITIALIZES THE
;STACK POINTER AND ZEROES THE MONITOR SCRATCH RAM.
INIT:	DI
	LD	HL,MEM	;POINT TO START OF MONITOR RAM
INIT1:	LD	(HL),0	;FILL 256 BYTE SPACE WITH ZEROS
	LD	SP,HL	;DO SOMETHING USEFUL & ADD DELAY
	INC	L	;On exit SP points to top of RAM
	JR	NZ,INIT1 ;LOOP TAKES ABOUT 4 msecs
;
;	INITIALIZE THE Z-80 FOR INTERRUPT MODE #2
	LD	A,H
	LD	I,A ;LOAD I REG WITH MSB OF VECTOR TABLE
	IM	2	; AND SELECT INTERRUPT MODE 2
;
	CALL	CLRSCN	;FILL THE CRT MEMORY WITH BLANKS
	IN	A,(BITDAT) ;Deselect CRT Bank
	RES	7,A
	OUT	(BITDAT),A
;
; STORE ANY NON-ZERO VALUES FOR VARIABLES IN MEMORY
;
	LD	HL,INTAB ;POINT TO DEFAULT VARIABLE TABLE
INIT2:	LD	B,0
	LD	C,(HL)	;BC=DATA BLOCK BYTECOUNT
	INC	HL
	LD	E,(HL)	;DE=DESTINATION FOR DATA
	INC	HL
	LD	D,(HL)
	INC	HL
	LDIR		;COPY DATA @ HL TO VARIABLES @ DE
	BIT	7,(HL)
	JR	Z,INIT2	;LOOP AGAIN IF NOT AT END OF TABLE
;
; INITIALIZE THE PROGRAMMABLE I/O DEVICES
	LD	DE,110H	;For copyright routine
;
	INC	HL	;POINT TO I/O INIT DATA TABLE
INIT3:	LD	B,(HL)	;B=INIT LOOP BYTECOUNT
	INC	HL
	LD	C,(HL)	;C=DEVICE CONTROL PORT#
	INC	HL
	OTIR		;SEND DATA @ HL TO PORT @ C
	BIT	7,(HL)	;TEST FOR TABLE END MARKER
	JR	Z,INIT3	;LOOP AGAIN IF NOT AT END
;
;	LD	BC,80H
;	CALL	SETDMA
;
;Now check the copyright integrity

	IF	ROM

	LD	B,47	;length of msg
	LD	HL,COPYRT-110H
	ADD	HL,DE
	XOR	A
LPP1	ADD	(HL)
	INC	HL
	DJNZ	LPP1
LPP2	CP	0E5H	;patch for each serial #
	JR	NZ,LPP2

	ENDIF	;ROM


;DETERMINE IF CONSOLE I/O CONFIGURATION WILL BE FOR THE
;ON-BOARD CRT AND KEYBOARD OR AN EXTERNAL SERIAL TERMINAL
;
;First enable keyboard interrupts
	IN	A,(KBDDAT)	;Clear any rubbish
	LD	A,10000011B
	OUT	(KBDCTL),A ;ENABLE INTERRUPTS FROM KBD PIO
	EI
;
	LD	C,SIOCPB
	LD	A,2	;Select read register 2
	OUT	(C),A
	IN	A,(C)	;TEST SIO READ REGISTER 2 TO see
;	CP	00000110B ; IF THE SIO IS INSTALLED (Status affected)
	CP	0	;See if SIO is installed (no status affects)
	JR	NZ,PARALL ;SKIP CONFIGURATION TEST IF NO SIO
	LD	B,00010000B ;B=RESET SIO EXTERNAL STATUS COMMAND  
DECIDE:	OUT	(C),B	;TEST FOR ARRIVAL OF A SERIAL
	IN	A,(C)	; INPUT CHARACTER START BIT
	BIT	4,A
	JR	NZ,BAUD	;EXIT LOOP IF START BIT DETECTED
	CALL 	KBDST	;See if FIFO has char ready from
	JR	Z,DECIDE ; parallel keyboard yet
PARALL:	CALL	KBDIN ;DISCARD FIRST KEYBOARD CHARACTER
	JR	SIGNON
;
;
; AUTOMATIC BAUD RATE SETTING ROUTINE FOR SIO
;
BAUD:	XOR	A
BAUD1:	OUT	(C),B
	IN	D,(C)	;READ SIO STATUS REGISTER
	BIT	4,D	;TEST THE SYNC/HUNT BIT
	JR	Z,BAUD1	;LOOP UNTIL IT CHANGES STATE
BAUD2:	INC	A
	OUT	(C),B	;RESET REGISTER #0 FLAGS AGAIN
	IN	D,(C)	;AND LOOP TIMING THE SYNC/HUNT BIT
	BIT	4,D
	JR	NZ,BAUD2 ;REPEAT UNTIL BIT CHANGES AGAIN

	IF	IS5MHZ
	RRA		;divide by 2
	ENDIF	;IS5MHZ

	LD	HL,RATES-1
BAUD3:	INC	HL	;INDEX INTO BAUD RATE TABLE
	RLA		; USING COUNT DERRIVED IN A
	JR	NC,BAUD3
	LD	A,(HL)	;GET BAUD RATE CONTROL BYTE FROM
	OUT	(BAUDB),A ;TABLE AND OUTPUT TO COM-8116 TIMER
	CALL	SIOIN	;DISARD FIRST SERIAL INPUT CHARACTER

	IF	SIO.INTERRUPT

	LD	A,1
	OUT	(SIOCPB),A ;RE-PROGRAM SIO B TO GENERATE
	LD	A,00011000B ;INTERRUPTS ON RECIEVED DATA,
	OUT	(SIOCPB),A ;PARITY DOES NOT AFFECT VECTOR
	LD	HL,SIOOUT
	LD	DE,KBDIN
	LD	BC,KBDST

	ENDIF	;SIO.INTERRUPT

	IF	SIO.POLLED

	LD	HL,SIOOUT
	LD	DE,SIOIN	;No  interrupts on input
	LD	BC,SIOST

	ENDIF	;SIO.POLLED

	CALL	SWITCH.STASH ;And modify vectors for VDU
;
;	PRINT SIGNON MESSAGE
;
SIGNON:
	IF	NOT ROM
	CALL	PNEXT
	DB	'(C) Copyright 1981 Trevor Marshall',EOT
	XOR	A
	LD	(UNIT),A	;So that warmboot logs in A:
	JP	WARMBOOT
	ENDIF	;NOT ROM

	IF	ROM
	DEFB	9,9,9,'... WAITAID monitor 4.1 ...'
	DEFB	CR,LF,LF,9,9
COPYRT: DB '(C) Copyright 1981 Trevor Marshall, serial # 00 ',EOT
;Now delay for Drive ready signal to go true
	LD	HL,2000H
LP1X:	DEC	HL
	EX	(SP),HL
	EX	(SP),HL
	LD	A,H
	OR	A,L	;Is HL zero?
	JR	NZ,LP1X
	LD	C,0	;Drive A
	CALL	SELECT
	AND	10011000B
	JR	NZ,ABORT.BOOT
	JP	BOOT	;Boot if ready
ABORT.BOOT: CALL LOGOFF ;Log off DSK not ready
	JP	PROMPT	;Else GO ENTER MONITOR
	ENDIF	;ROM
;
;
; BAUD RATE CONSTANTS FOR COM 8116 BAUD RATE GENERATOR
;
RATES:	DEFB	0101B		;   300 BAUD
	DEFB	0110B		;   600 BAUD
	DEFB	0111B		;  1200 BAUD
	DEFB	1010B		;  2400 BAUD
	DEFB	1100B		;  4800 BAUD
	DEFB	1110B		;  9600 BAUD
	DEFB	1111B		; 19200 BAUD
	DEFB	1111B		; 19200 BAUD
;
;
INTAB	EQU	$	;INITIALIZATION DATA TABLES
;
;
;INITIALIZE THE Z-80 'I' REGISTER INTERRUPT VECTOR TABLE
;
	DEFB	2
	DEFW	SYSVEC+2
	DEFW	KEYSRV ;PARALLEL KEYBOARD INTERRUPT VECTOR
;
	DEFB	2
	DEFW	CTCVEC+6
	DEFW	TIMER ;ONE SECOND TIMER INTERRUPT VECTOR

;	IF 	SIO.INTERRUPT

	DEFB	4
	DEFW	SIOVEC+4
	DEFW	SIOINT	;SIO RECIEVE INTERRUPT VECTOR
	DEFW	SIOERR	;SIO PARITY, OVERRUN & FRAMING ERROR

;	ENDIF	;SIO.INTERRUPT

;
;	INITIALIZE DISK I/O DRIVER VARIABLES
;
	DEFB	6
	DEFW	UNIT
	DEFB	255	;FLAG ALL DRIVES AS DE-SELECTED
	DEFB	255,255	;CLEAR HEAD POSITION TABLE
	DEFB	0,0	;Both default single density
	DEFB	30	;SET MOTOR TURN-OFF TIMER
; RECLEN, TRACK & SECTOR not initialized
;
;	INITIALIZE THE CRT DISPLAY CURSOR
;
	DEFB	2
	DEFW	CHRSAV
	DEFB	' '
	DEFB	'_'+80H	;USE BLINKING UNDERSCORE
;
;	SET DEFAULT 'SOFTWARE' INTERRUPT VECTORS
;
	DEFB	6
	DEFW	TIKVEC
	DEFW	DSKTMR ;POINT 'TIKVEC' TO DISK MOTOR TIMER
	DEFW	STASH ;POINT 'PINVEC' TO FIFO INPUT ROUTINE
	DEFW	STASH ;POINT 'SINVEC' TO FIFO INPUT ROUTINE
;
;
	DEFB	-1	;END OF VARIABLE INIT TABLE
;
;
;
BAUDA	EQU	00H ;CHANEL A BAUD RATE GENETATOR
SIO	EQU	04H ;DUAL SERIAL I/O
GENPIO	EQU	08H ;GENERAL PURPOSE PARALLEL I/O
BAUDB	EQU	0CH ;CHANEL B BAUD RATE GENERATOR
WD1771	EQU	10H ;WESTERN DIGITAL DISK CONTROLLER
SCROLL	EQU	14H ;CRT SCROLL MEMORY SCROLL REGISTER
CTC	EQU	18H ;QUAD COUNTER/TIMER CIRCUIT
SYSPIO	EQU	1CH ;SYSTEM PARALLEL I/O
;
;INITIALIZE SYSTEM PIO FOR USE AS BANK-SWITCH,
; DISK DRIVE SELECT AND PARALLEL KEYBOARD INPUT
;
BITDAT	EQU	SYSPIO+0
BITCTL	EQU	SYSPIO+1
KBDDAT	EQU	SYSPIO+2
KBDCTL	EQU	SYSPIO+3

	IF	ROM
	DEFB	3,BITCTL
	DEFB	11001111B ;PUT SYSTEM PIO IN BIT MODE
	DEFB	00000000B ;All bits outputs
	DEFB	01000000B ;DISABLE INTERRUPTS
;
	DEFB	1,BITDAT
	DEFB	00001000B ;DE-SELECT ROMS, ENABLE DRIVE 0
;			; DD* line high
;			; SIDE 2 line low (=side 1)
	ENDIF	;ROM
;
	DEFB	2,KBDCTL
	DEFB	01001111B ;PUT KEYBOARD PORT IN INPUT MODE
	DEFB	[SYSVEC+2] AND 0FFH ;LOAD KEYBOARD INTERRUPT VECTOR
;
;Set up printer PIO
PIOADAT: EQU	8
PIOACTL: EQU	9
PIOBDAT: EQU	10
PIOBCTL: EQU	11
;
	IF	PARALLEL AND NOT ROM
	DB	3,PIOACTL
	DB	3	;Disable interrupts
	DB	0CFH	;Put in bit mode
	DB	0	;All outputs
;
	DB	3,PIOBCTL
	DB	3	;Disable interrupts
	DB	0CFH	;Bit mode
	DB	0FH	;Bits 1-3 inputs
;
	DB	1,PIOBDAT ;Set bit 7 for parallel printer
	DB	80H	 ;reset special bell (bit 6)
	ENDIF	;PARALLEL
;
;	INITIALIZE CHANELS 2 AND 3 OF THE CTC
;	TO GENERATE ONE SECOND INTERRUPTS FROM CTC3
;
CTC0	EQU	CTC+0		;CTC CHANEL 0 PORT#
CTC1	EQU	CTC+1		;CTC CHANEL 1
CTC2	EQU	CTC+2		;CTC CHANEL 2
CTC3	EQU	CTC+3		;CTC CHANEL 3

	DEFB	1,CTC0
	DEFB	[CTCVEC] AND 0FFH ;BASE INTERRUPT VECTOR FOR CTC
;
	IF	ROM
	DEFB	2,CTC2
	DEFB	00100111B ;PUT CTC2 IN TIMER MODE
	DEFB	105	;CTC2 PERIOD=105*256*400 nsecs
;
	DEFB	2,CTC3
	DEFB	11000111B ;PUT CTC3 IN COUNTER MODE
	DEFB	93    ;CTC3 PERIOD=999936 MICROSECONDS
	ENDIF	;ROM
;
;
;INITIALIZE SIO CHANEL B FOR ASYNCHRONOUS SERIAL
; INTERFACE TO PRINTER OR TERMINAL
;
SIODPA	EQU	SIO+0	;SIO DATA PORT A
SIODPB	EQU	SIO+1	;SIO DATA PORT B
SIOCPA	EQU	SIO+2	;SIO CONTROL/STATUS PORT A
SIOCPB	EQU	SIO+3	;SIO CONTROL/STATUS PORT B

	IF	ROM
	DEFB	1,BAUDB
	DEFB	0101B ;SET COM 8116 FOR 300 BAUD DEFAULT

	DEFB	10,SIOCPB
	DEFB	4	  ;SELECT REGISTER #4
	DEFB	01001100B ;16X CLOCK, 2 STOP BIT, NO PARITY
	DEFB	1	  ;SELECT REGISTER #1
	DEFB	00000000B ;STATUS NO EFFECT ON VECTOR, NO INTERRUPTS
	DEFB	3	  ;SELECT REGISTER #3
	DEFB	11000001B ;8 BITS/RX CHARACTER,NO AUTO ENABLES
	DEFB	5	  ;SELECT REGISTER #5
	DEFB	11101010B ;8 BITS/TX CHARACTER, ASSERT DTR
	DEFB	2	  ;SELECT REGISTER #2
	DEFB	[SIOVEC] AND 0FFH ;LOAD INTERRUPT VECTOR BASE

	ELSE
	DB	4,SIOCPB
	DB	1
	DB	0	;status no effect
	DB	2
	DB	[SIOVEC] AND 0FFH

	ENDIF	;ROM

	DEFB	-1		;END-OF-TABLE
;
;	INIT DONE
;
;
;************************************************
;*						*
;* BASIC HEX MONITOR FOR Z-80 PROCESSORS	*
;*				3-Aug-80	*
;*						*
;************************************************
;
	IF	ROM	;Only want the monitor in ROM
;
;
;
PROMPT:	LD	SP,[MEM+0FFH]
	CALL	PNEXT
	DEFB	CR,LF
	DEFB	'* '
	DEFB	EOT
	LD	HL,LINBUF
	LD	C,32
	CALL	GETLIN	;INPUT A BUFERED CONSOLE LINE
	JR	C,WHAT	;PRINT 'WHAT ?' IF INPUT ERROR

	XOR	A
	LD	(ESCFLG),A
	CALL	CRLFS
	LD	A,(LINBUF) ;GET FIRST CHARACTER IN LINE
	CP	CR
	JR	Z,PROMPT ;JUMP IF A NULL LINE
	LD	HL,CMDTAB ;SEARCH FOR A MATCHING CHARACTER
	LD	BC,CMDSIZ/3 ; IN COMMAND SEARCH TABLE
	CALL	SEARCH
	JR	NZ,WHAT	;TRY AGAIN IF SEACRH FAILS
	PUSH	BC
	LD	IY,LINBUF+1
	CALL	PARAMS	;INPUT NUMERIC PARAMETERS FROM
	POP	IX	; LINE BUFFER AND TEST IF ERROR
	JR	C,WHAT
	LD	HL,(PARAM1)
	LD	DE,(PARAM2)
	LD	BC,(PARAM3)
	CALL	CALLX	;CALL SUBROUTINE @ IX
	JR	NC,PROMPT ;GO BACK TO PROMPT IF NO ERRORS

WHAT:	CALL	PNEXT
	DEFB	' what ?'
	DEFB	'G'-64	;SAY 'what ?' AND BEEP THE BELL
	DEFB	EOT
	JR	PROMPT
;
;
CALLX:	JP	(IX)	;CALL SUBROUTINE @ IX
;
;
;
CMDTAB:	DEFB	'R'
	DB	'W'
	DEFB	'O'
	DEFB	'E'
	DEFB	'G'
	DEFB	'T'
	DEFB	'F'
	DEFB	'S'
	DEFB	'M'
	DEFB	'B'
	DEFB	'D'
	DEFB	'S'

	DEFW	SWITCH	;SWITCH CONSOLE OUTPUT VECTOR
	DEFW	MEMDMP	;DUMP MEMORY IN HEX/ASCII
	DEFW	BOOT	;BOOT UP CP/M
	DEFW	BLOCK	;MEMORY BLOCK MOVE
	DEFW	VIEW	;MEMORY EXAMINE/CHANGE
	DEFW	FILL	;FILL MEMORY
	DEFW	TEST	;RAM DIAGNOSTIC
	DEFW	GOTO	;JUMP TO MEMORY LOCATION
	DEFW	INCMD	;READ FROM INPUT PORT
	DEFW	OUTCMD	;WRITE TO OUTPUT PORT
	DW	WRITE.SECTOR
	DEFW	READ.SECTOR ;DISPLAY DISK SECTOR DATA

CMDSIZ	EQU	$-CMDTAB
;
;
;************************************************
;*						*
;* MONITOR COMMAND ACTION ROUTINES PACKAGE	*
;*						*
;************************************************
;
;
;
;
;
; -- DISK BOOT LOADER COMMAND --
;
BOOT:	LD	C,0	;SELECT DRIVE 0 FOR BOOT LOAD
	CALL	SELECT
	JR	NZ,DSKERR
	CALL	LOGIN	;HOME HEAD TO TRACK 0 and login disk
	JR	NZ,DSKERR ;ERROR IF NOT READY OR AT TR0
	LD	BC,0080H ;POINT TO CP/M READ BUFFER
	CALL	SETDMA
	LD	C,1	;SELECT SECTOR 1
	CALL	SETSEC
	CALL	READ	;READ TRACK 0/ SECTOR 1
	JR	NZ,DSKERR
	JP	0080H	;GO EXECUTE LOADER
;
;
; ---- Write Disk Sector-----
WRITE.SECTOR: LD  C,A  ;Save param count
	LD	A,0FFH
	LD	(RWFLAG),A ;If flag is non zero = write
	JR	RW11
;	-- DISK SECTOR READ COMMAND --
;
READ.SECTOR: LD	C,A ;Save param count
	XOR A
	LD	(RWFLAG),A ; flag is zero if read
RW11:	LD	A,C
	CP	3	;CHECK PARAMETER COUNT
	SCF
	RET	NZ
	CALL	LOGOFF	;Logoff current disk
	LD	HL,PARAM1
	LD	C,(HL)	;USE FIRST ARG AS UNIT#
	CALL	SELECT
	JR	NZ,DSKERR
	LD	HL,PARAM2
	LD	C,(HL)	;USE SECOND ARG AS TRACK#
	CALL	SEEK
	JR	NZ,DSKERR
	LD	HL,PARAM3
	LD	C,(HL)	;USE THIRD ARG AS SECTOR#
	CALL	SETSEC
	LD	BC,0080H ;** Default DSK buffer for BOOT
	CALL	SETDMA
	LD	A,(RWFLAG)
	OR	A
	JR	Z,RD1
	CALL	WRITE
	JR	NZ,DSKERR ;Will need to catch this during LOGON
	RET
RD1:	CALL	READ
	JR	NZ,DSKERR
;	LD	HL,0080H
;	LD	DE,8
;	JP	DUMP	;DUMP DISK READ BUFFER AND RETURN
	RET
;
;
DSKERR:	LD	C,A	;SAVE 1771 STATUS
	CALL	PNEXT
	DEFB	'disk error ',EOT
	LD	A,C	;get status
	CALL	PUT2HS
	RET
;
;
;	-- MEMORY DUMP COMMAND --
;
MEMDMP:	DEC	A	;CHECK PARAMETER COUNT
	JR	Z,MDMP2
	DEC	A
	JR	Z,MDMP3
MDMP1:	LD	HL,(LAST)
MDMP2:	LD	DE,8	;Default # of lines dumped
	JR	MDMP3B

MDMP3:	EX	DE,HL
	SBC	HL,DE	;DERRIVE BYTECOUNT FOR DUMP RANGE
	LD	B,4
MDMP3A:	SRL	H	;DIVIDE BYTECOUNT BY 16
	RR	L
	DJNZ	MDMP3A
	INC	HL
	EX	DE,HL
MDMP3B:	CALL	DUMP	;DUMP DE*16 BYTES STRTING AT HL
	LD	(LAST),HL
	RET	
;
;
DUMP:	PUSH	HL	;SAVE STARTING ADDRESS
	CALL	PUT4HS	;PRINT STARTING ADDRESS IN HEX
	CALL	SPACE
	LD	B,16
DUMP2:	LD	A,(HL)	;GET A DATA BYTE @ HL
	INC	HL
	CALL	PUT2HS	;PRINT THE DATA IN HEX
	DJNZ	DUMP2	;REPEAT 16 TIMES
	POP	HL	;RESTORE STARTING ADDRESS
	LD	B,16
DUMP3:	LD	A,(HL)	;GET BACK DATA BYTE @ HL
	INC	HL
	RES	7,A
	CP	20H
	JR	C,DUMP4
	CP	7FH
	JR	C,DUMP5
DUMP4:	LD	A,'.'	;PRINT A DOT IF DATA < 20 OR > 7F
DUMP5:	CALL	OUTPUT	;PRINT ASCII CHARACTER IN A
	DJNZ	DUMP3
	CALL	CRLFS
	RET	NZ	;EXIT IF ESCAPE REQUEST IS INDICATED
	DEC	DE
	LD	A,D
	OR	E
	JR	NZ,DUMP
	RET
;
;
;
;
;	-- MEMORY EXAMINE COMMAND --
;
VIEW:	CALL	MDATA
	CALL	ECHO
	CP	CR
	JR	Z,VIEW4
	CP	'-'
	JR	Z,VIEW5
VIEW2:	CALL	ASCHEX
	CCF	
	RET	NC
	RLCA	
	RLCA	
	RLCA	
	RLCA	
	LD	C,A
	CALL	ECHO
	CALL	ASCHEX
	CCF	
	RET	NC
	OR	C
VIEW3:	LD	(HL),A
	CALL	CHECK
VIEW4:	INC	HL
	INC	HL
VIEW5:	DEC	HL
	JR	VIEW
;
;
;
;	-- JUMP TO MEMORY LOCATION COMMAND --
;
; We should fill the registers from RAM
GOTO:	DEC	A	;CHECK PARAMETER COUNT
	SCF
	RET	NZ
	PUSH	HL
	POP	IX
;Set up the jump at 30H for breakpoints (F7H,RST6)
	LD	A,0C3H
	LD	HL,30H
	LD	(HL),A
	INC	HL
	LD	A,[BKPT AND 0FFH]
	LD	(HL),A
	INC	HL
	LD	A,[BKPT SHR 8]
	LD	(HL),A
; OK, now go to routine
	JP	(IX)
;	OR	A
;	RET	;RETURN IF WE GET BACK AGAIN
;
BKPT:	PUSH	HL
	PUSH	DE
	PUSH	BC
	PUSH	AF
	CALL	PNEXT
	DB	'**Breakpoint**, regs are: ',EOT
	POP	AF
	CALL	PUT2HS	;Put acc
	POP	HL
	CALL	PUT4HS	;BC
	POP	HL
	CALL	PUT4HS	;DE
	POP	HL
	CALL	PUT4HS	;HL
	CALL	PNEXT
	DB	',PC=',EOT
	POP	HL
	DEC	HL	;To allow for PC incr past RST30 
	CALL	PUT4HS	;PC
	CALL	CRLFS
	JP	PROMPT
;
;
;	-- MEMORY READ/WRITE DIAGNOSTIC COMMAND --
;
TEST:	CP	2	;CHECK PARAMETER COUNT
	SCF
	RET	NZ
	INC	DE
	LD	E,D	;GET ENDING PAGE ADDRESS INTO E
	LD	D,H	;GET STARTING PAGE ADDRESS INTO D
	LD	B,0	;INITIALIZE PASS COUNTER
TEST1:	LD	H,D	;POINT HL TO START OF BLOCK
	LD	L,0
TEST2:	LD	A,L
	XOR	H	;GENERATE TEST BYTE
	XOR	B
	LD	(HL),A	;STORE BYTE IN RAM
	INC	HL
	LD	A,H
	CP	E	;CHECK FOR END OF TEST BLOCK
	JR	NZ,TEST2
;			NOW READ BACK EACH BYTE & COMPARE
	LD	H,D
	LD	L,0	;POINT HL BACK TO START
TEST3:	LD	A,L
	XOR	H	;RE-GENERATE TEST BYTE DATA
	XOR	B
	CALL	CHECK	;VERIFY MEMORY DATA STILL GOOD
	RET	NZ	;EXIT IF ESCAPE REQUEST IS INDICATED
	INC	HL	; ELSE GO ON TO NEXT BYTE
	LD	A,H
	CP	E	;CHECK FOR END OF BLOCK
	JR	NZ,TEST3
	INC	B	;BUMP PASS COUNT
	LD	A,'+'
	CALL	OUTPUT	;PRINT '+' AND ALLOW FOR EXIT
	JR	Z,TEST1	;DO ANOTHER PASS IF NO ESCAPE
	RET
;
;
;
CHECK:	CP	(HL)
	RET	Z	;RETURN IF (HL)=A
	PUSH	AF
	CALL	MDATA	;PRINT WHAT WAS ACTUALLY READ
	CALL	PNEXT
	DEFB	'should='
	DEFB	EOT
	POP	AF
	JP	PUT2HS	;PRINT WHAT SHOULD HAVE BEEN READ
;
;
MDATA:	CALL	CRLFS
	CALL	PUT4HS
	LD	A,(HL)
	JP	PUT2HS
;
;
;
;	-- FILL MEMORY WITH CONSTANT COMMAND --
;
FILL:	CP	3	;CHECK IF PARAMETER COUNT=3
	SCF	
	RET	NZ
FILL1:	LD	(HL),C
	PUSH	HL
	OR	A
	SBC	HL,DE	;COMPARE HL TO END ADDRESS IN DE
	POP	HL
	INC	HL	;ADVANCE POINTER AFTER COMPARISON
	JR	C,FILL1
	RET
;
;
;
;
;	-- MEMORY BLOCK MOVE COMMAND --
;
BLOCK:	CP	3	;CHECK IF PARAMETER COUNT=3
	SCF	
	RET	NZ
	CALL	BLOCAD
	LD	A,C
	OR	B
	RET	Z	;EXIT NOW IF BC=0
	LDIR
	RET
;
;
;
BLOCAD:	EX	DE,HL
	OR	A	;CLEAR CARRY
	SBC	HL,DE	;GET DIFFRENCE BETWEEN
	EX	DE,HL	;HL & DE FOR BYTECOUNT
	PUSH	DE
	PUSH	BC
	POP	DE	;GET OLD BC INTO DE
	POP	BC
	INC	BC	;GET COUNT+1 INTO BC
	RET	
;
;
;
;
;	-- READ FROM INPUT PORT COMMAND --
;
INCMD:	DEC	A	;CHECK IF PARAMETER COUNT=1
	SCF
	RET	NZ
	LD	C,L	;POINT C TO INPUT PORT
IN1:	CALL	CRLFS
	LD	A,C
	CALL	PUT2HS
	IN	A,(C)
	CALL	PUT2HS
	CALL	ECHO
	CP	CR
	JR	Z,IN2
	CP	'-'
	JR	Z,IN3
	OR	A
	RET
 
IN2:	INC	C
	INC	C
IN3:	DEC	C
	JR	IN1
;
;
;
;	-- WRITE TO OUTPUT PORT COMMAND --
; Syntax: O <Data> <Port>
OUTCMD:	CP	2	;CHECK IF PARAMETER COUNT=2
	SCF
	RET	NZ
	LD	C,E	;POINT C TO OUTPUT PORT
	OUT	(C),L	;OUTPUT DATA PASSED IN L
	OR	A
	RET
;
;
;	-- SWITCH CONSOLE OUTPUT DEVICE COMMAND --
;
SWITCH:	LD	HL,COFLAG
	INC	(HL)	;TOGGLE CONSOLE OUTPUT TYPE FLAG
	BIT	0,(HL)

	IF	SIO.POLLED
	LD	DE,SIOIN	;No interrupts
	LD	BC,SIOST
	LD	HL,SIOOUT
	ELSE
	LD	DE,KBDIN	;Interrupts
	LD	BC,KBDST
	LD	HL,SIOOUT
	ENDIF	;SIO.POLLED

	JR	Z,SWIT2	;JUMP IF ZERO TO ONE TRANSITION
	LD	HL,CRTOUT
	LD	DE,KBDIN
	LD	BC,KBDST

	ENDIF	;ROM (over all the monitor package)

SWITCH.STASH: ;Store the Serial/Parallel vectors
SWIT2:	LD	(CONOUT+1),HL ;STORE NEW CONSOLE OUTPUT ADDRESS
	LD	(CONIN+1),DE
	LD	(CONST+1),BC
	RET
;
;
;************************************************
;*						*
;* CONSOLE I/O PACKAGE AND UTILITY ROUTINES	*
;*						*
;************************************************
;
	IF	ROM
;
;
GETLIN:	LD	B,C	;SAVE MAX LINE LENGTH PARAMETER IN B
GLIN1:	CALL	ECHO	;GET A CHARACTER FROM THE CONSOLE
	CP	CR	;CHECK FOR CARRIAGE RETURN
	JR	Z,GLIN2
	CP	'H'-64	;CHECK FOR CTL-H BACKSPACE
	JR	Z,GLIN4
; Check for delete here will not work, catch it at CHIN
;	CP	7FH	;Check fo delete
;	JR	Z,GLIN4
	CP	' '
	RET	C	;OTHER CONTROL CHARACTERS ARE ILLEGAL
	LD	(HL),A
	INC	HL	;STORE CHARACTER IN BUFFER
	DEC	C
	JR	NZ,GLIN1 ;GET ANOTHER IF THERE'S MORE ROOM
	SCF	
	RET		;RETURN WITH CARRY=1 IF TOO
			;MANY CHARACTERS ARE ENTERED
GLIN2:	LD	(HL),A	;PUT CARRIAGE RETURN ON END OF LINE
	RET		;RETURN WITH CARRY BIT=0
 
GLIN4:	DEC	HL	;DELETE LAST CHARACTER FROM BUFFER
	CALL	PNEXT
	DEFB	' ','H'-64 ;PRINT A SPACE TO OVERWRITE THE
	DEFB	EOT	; LAST CHARACTER, THEN DO A BACKSPACE
	INC	C
	LD	A,B	;MAKE SURE YOU'RE NOT TRYING TO
	SUB	C	;BACKSPACE PAST THE START OF THE LINE
	JR	NC,GLIN1
	RET	
;
;
;
PARAMS:	LD	BC,0
	LD	A,(IY+0)
LOOKAGAIN: CP	CR	;CHECK IF LINE TERMINATES
	JR	Z,ZEROLENGTH ;Immediately with <CR>
	CP	' '	 ;Or begins with a space
	JR	Z,DROPIT
	CP	'M'	 ;Or an M
	JR	Z,DROPIT
	JR	PARA2
DROPIT:	INC	IY
	LD	A,(IY)
	JR	LOOKAGAIN
;
ZEROLENGTH: XOR	A
	RET		;RETURN WITH PARAM COUNT=0 IF SO
; 
PARA1:	INC	C
	INC	C
	BIT	3,C
	SCF
	RET	NZ	;ERROR IF > 4 NUMBERS ENTERED
PARA2:	PUSH	BC	;SAVE PARAMETER COUNT
	CALL	GETHEX	;READ A NUMBER FROM LINE BUFFER
	POP	BC
PARA4:	RET	C	;ERROR IF RESULT OVER 16 BITS
	LD	IX,PARAM1 ;POINT TO PARAMETER STORAGE AREA
	ADD	IX,BC	;ADD PARAMETER COUNT IN BC
	LD	(IX+0),L
	LD	(IX+1),H ;STORE DATA RETURNED FROM 'GETHEX'
	CP	' '
	JR	Z,PARA1	;GET ANOTHER ITEM IF SPACE
	CP	','
	JR	Z,PARA1	;GET ANOTHER ITEM IF COMMA
	CP	CR
	SCF		;ELSE CHECK FOR CARRIAGE RETURN
	RET	NZ	; AND EXIT WITH CY=1 IF NOT
PAREND:	LD	A,C
	SRL	A	;A=COUNT OF NUMBERS ENTERED
	INC	A
	RET
;
;	GETHEX CONVERTS ASCII TO BINARY AND DOES
;	HIGH LIMIT CHECKS TO LESS THAN 17 BITS.
;	CARRY SET ON ILLEGAL CONVERSION RESULT
;	TERMINATING CHARACTER RETURNS IN A.
;	HL RETURNS WITH 16 BIT BINARY INTEGER
;
GETHEX:	LD	HL,0
	JR	GNUM3
	
GNUM1:	LD	B,4
GNUM2:	ADD	HL,HL	;MULTIPLY RESULT BY 16
	RET	C	;RETURN IF IT OVERFLOWS 16 BITS
	DJNZ	GNUM2
	LD	E,A	;APPEND NEW LOW ORDER DIGIT
	LD	D,0	;AND GET RESULT BACK INTO DE
	ADD	HL,DE
	RET	C	;RETURN IF OVERFLOW
GNUM3:	LD	A,(IY+0) ;GET A CHARACTER FROM LINE INPUT
	INC	IY	; BUFFER @ IY AND BUMP IY
	LD	C,A
	CALL	ASCHEX	;CONVERT ASCII TO NUMERIC
	JR	NC,GNUM1
	LD	A,C
	OR	A
	RET
;
;
ASCHEX:	SUB	'0'
	RET	C
	CP	10
	CCF
	RET	NC
	SUB	7
	CP	10
	RET	C
	CP	16
	CCF
	RET
;

	ENDIF	;ROM

;
;
SEARCH:	CPIR		;SEARCH TABLE @HL FOR MATCH WITH A
	RET	NZ	;EXIT NOW IF SEARCH FAILS
	ADD	HL,BC
	ADD	HL,BC	;ADD RESIDUE FROM CPIR BYTECOUNT
	ADD	HL,BC	; TO HL 3 TIMES TO GET POINTER
	LD	C,(HL)	; TO ADDRESS PART OF TABLE ENTRY
	INC	HL
	LD	B,(HL)
	RET		;EXIT WITH Z=1 TO INDICATE MATCH
;
;
;
PUT4HS:	LD	A,H
	CALL	PUT2HX
	LD	A,L
PUT2HS:	CALL	PUT2HX
	JR	SPACE
;
;
PUT2HEX:
PUT2HX:	PUSH	AF
	RRA	
	RRA	
	RRA	
	RRA	
	CALL	PUTNIB
	POP	AF
PUTNIB:	AND	00001111B
	ADD	A,90H
	DAA
	ADC	A,40H
	DAA
	JR	OUTPUT
;
;
; PMSG PRINTS THE STRING OF ASCII CHARACTERS
;  POINTED TO BY THE RELATIVE ADDRESS IN DE
;  UNTIL AN EOT IS ENCOUNTERED IN THE STRING.
;
EOT	EQU	04H
CR	EQU	0DH
LF	EQU	0AH
;
	
PNEXT:	EX	(SP),HL
	CALL	PMSG
	EX	(SP),HL
	RET
;
PMSG:	LD	A,(HL)
	INC	HL
	CP	EOT
	RET	Z
	CALL	OUTPUT
	JR	PMSG
;
;
; CRLFS OUTPUTS A RETURN-LINEFEED-SPACE
;  TO THE CONSOLE DEVICE
;
CRLFS:	CALL	PNEXT
	DEFB	CR,LF,EOT
SPACE:	LD	A,' '
	JR	OUTPUT
;
	IF	ROM
;
;
; ECHO INPUTS ONE CHARACTER FROM THE CONSOLE
; DEVICE, PRINTS IT ON THE CONSOLE OUTPUT AND
; THEN RETURNS IT IN REGISTER A WITH BIT 7 RESET
;
; OUTPUT PRINTS THE CHARACTER IN REGISTER A ON
; THE CONSOLE OUTPUT DEVICE AND THEN DOES A CHECK
; FOR CONSOLE INPUT TO FREEZE OR ABORT OUTPUT.
;

ECHO:	CALL	CONIN	 ;INPUT A CHARACTER AND ECHO IT
	CP	7FH	;delete?
	JR	NE,ECHO2
	LD	A,8	;yes, load a backspace
ECHO2:	PUSH	AF
	CALL	CONOUT	
	POP	AF
	CP	'Z'+1	;make LC UC
	RET	C
	SUB	20H	;CONVERT UPPER CASE TO LOWER CASE
	RET
;
	ENDIF	;ROM
;
;
OUTPUT:	CALL	CONOUT
	CALL	CONST	;SEE IF CONSOLE INPUT IS PENDING
	JR	Z,OUTP2
	CALL	CONIN
	CP	CR	;SEE IF CARRIAGE RETURN WAS TYPED
	JR	Z,OUTP1
	CP	'S'-40H ;<CTL-S>?
	JR	NZ,OUTP3 ;If not put the char back on the queue
	CALL	CONIN	;WAIT FOR ANOTHER INPUT CHARACTER
	JR	OUTP2	; THEN RETURN TO CALLING ROUTINE

OUTP1:	LD	(ESCFLG),A ;SET ESCAPE FLAG TO NON-ZERO VALUE
OUTP2:	LD	A,(ESCFLG)
	OR	A	;RETURN CURRENT STATUS OF ESCAPE
	RET		; FLAG TO CALLING ROUTINE
;
OUTP3:	PUSH	HL
	CALL	REPLACE
	POP	HL
	JR	OUTP2	;and return
;
;
;********************************************************
;*							*
;*	INTERRUPT SERVICE ROUTINES FOR KEYBOARD		*
;*	INPUT AND REAL-TIME CLOCK FUNCTIONS		*
;*				3-Aug-80		*
;*							*
;********************************************************
;
;
;
;
KBDST:	LD	A,(FIFCNT)	;GET INPUT FIFO BYTECOUNT
	OR	A		;TEST IF EQUAL ZERO
	RET	Z		;EXIT WITH A=0 IF QUEUE IS EMPTY
	LD	A,255
	RET			;ELSE SET A=255 TO INDICATE DATA READY
;
;
;
KBDIN:	CALL	KBDST
	JR	Z,KBDIN	;LOOP UNTIL KEYBOARD INPUT READY
	PUSH	HL
	CALL	REMOVE		;GET CHARACTER FROM INPUT QUEUE
	POP	HL
	BIT	7,A
;If B7 set it must be HONEYWELL KYBD char code
	RET	Z
; Else catch the cursor codes for Honeywell keyboard
; 81=UP, 83=right, 84=left, 82=down, 88= home
; E7=CLRSCREEN is caught in STASH immediately it is pressed
	PUSH	HL
	PUSH	BC
	LD	HL,TBL1	;Point at keycode table
	LD	BC,TBL1.LENGTH
	CPIR	;Until match or no match
	JR	NZ,RET1	;Abort if no match
	LD	BC,TBL1.LENGTH ;Point into next table
	ADD	HL,BC
	LD	A,(HL)
RET1:	POP	BC
	POP	HL
	RET
;
TBL1:	DB	81H
	DB	83H
	DB	84H
	DB	82H
	DB	0FFH	;a dummy entry for debugging
TBL1.LENGTH: EQU $-TBL1
	DB	0 ;1 byte spare as HL is incremented past match
TBL2:	DB	05H	;Up line
	DB	04H	;Right char
	DB	13H	;Left char
	DB	18H	;Down line
	DB	0	;our dummy entry again
;HOME is not processed, as not used by WORDSTAR
;
;
;
; We don't need software shift lock
STASH:;	LD	HL,LOCK		;POINT TO SHIFT LOCK VARIABLES
;	CP	(HL)		;TEST IF A=SHIFT LOCK CHARACTER
;	INC	HL		; THEN POINT TO LOCK FLAG
;	JR	NZ,STASH2	;JUMP IF NOT THE SHIFT CHARACTER
;	INC	(HL)		; ELSE COMPLIMENT THE SHIFT LOCK
;	RET			; AND EXIT NOW
;
STASH2:;	BIT	0,(HL)		;TEST THE SHIFT LOCK FLAG
;	JR	Z,STASH3	;JUMP IF SHIFT LOCK NOT SET
;	CP	40H		;ELSE CHECK FOR SHIFABLE CHARACTER
;	JR	C,STASH3	; AND JUMP IF NOT EQUAL OR GREATER
;	CP	7FH		; THAN '@' AND LESS THAN RUBOUT
;	JR	NC,STASH3
;	XOR	00100000B	;ELSE TOGGLE BIT 5 OF THE CHARACTER
STASH3:	LD	C,A
;
	CP	0E7H	;Honeywell Keyboard's CLEAR SCREEN key
	JR	NZ,NXT35
	CALL	CLRSCN
	IN	A,(BITDAT)
	RES	7,A
	OUT	(BITDAT),A
	RET
;
NXT35:	LD	HL,FIFCNT	;BUMP INPUT FIFO CHARACTER COUNT
	LD	A,(HL)
	INC	A
	CP	16
	RET	NC		;EXIT NOW IF FIFO IS FULL
	LD	(HL),A		; ELSE INCREMENT FIFO COUNT
	LD	HL,FIFIN	;POINT HL TO FIFO INPUT OFFSET
	CALL	INDEX
	LD	(HL),C		;STORE CHARACTER IN FIFO @ HL
	RET
;
;
;
;
REMOVE:	LD	HL,FIFCNT
	DEC	(HL)
	LD	HL,FIFOUT	;POINT HL TO FIFO OUTPUT OFFSET
INDEX:	LD	A,(HL)
	INC	A
	AND	00001111B	;INCREMENT FIFO POINTER
	LD	(HL),A		; MODULO 16 AND REPLACE
	LD	HL,FIFO
	ADD	A,L		;INDEX INTO FIFO BY OFFSET IN A
	LD	L,A
	LD	A,(HL)
	RET
;
REPLACE: LD	HL,FIFCNT
	INC	(HL)
	LD	HL,FIFOUT
INDEX2:	LD	A,(HL)
	DEC	A
	AND	00001111B
	LD	(HL),A
	RET
;
;
;	SOFTWARE DISK MOTOR TURN-OFF TIMER ROUTINE
;
DSKTMR:	LD	HL,MOTOR	;DECREMENT DISK TURN-OFF TIMER
	DEC	(HL)
	RET	NZ		;EXIT IF NOT TIMED OUT YET
	IN	A,(BITDAT)
	OR	00000100B	;DISABLE ALL DRIVE SELECTS AND
	OUT	(BITDAT),A	; TURN OFF THE SPINDLE MOTORS
	RET
;
;
;	-- INTERRUPT SERVICE ROUTINE FOR PARALLEL KEYBOARD --
;
KEYSRV:	LD	(SPSAVE),SP	;SAVE USER STACK POINTER AND
	LD	SP,TMPSTK+16	; SWITCH TO LOCAL STACK
	PUSH	HL
	PUSH	DE
	PUSH	BC
	PUSH	AF		;SAVE MACHINE STATE
	IN	A,(KBDDAT)	;READ KEYBOARD INPUT PORT
	CPL
	LD	HL,(PINVEC)	;GET KBD INTERRUPT ROUTINE VECTOR
	JR	DSPTCH	; AND JUMP TO DISPATCH POINT
;
;
;
;	-- INTERRUPT SERVICE ROUTINE FOR ONE SECOND TIMER --
;
TIMER:	LD	(SPSAVE),SP	;SAVE USER STACK POINTER AND
	LD	SP,TMPSTK+16	; SWITCH TO LOCAL STACK
	PUSH	HL
	PUSH	DE
	PUSH	BC
	PUSH	AF
	LD	HL,(TIKVEC)	;GET CLOCK INTERRUPT ROUTINE VECTOR
	JR	DSPTCH	; AND JUMP TO DISPATCH POINT
;
;
;
;	-- SERIAL INPUT INTERRUPT SERVICE ROUTINE FOR SIO --
;
	IF	SIO.INTERRUPT

SIOINT:	LD	(SPSAVE),SP	;SAVE USER STACK POINTER AND
	LD	SP,TMPSTK+16	; SWITCH TO LOCAL STACK
	PUSH	HL
	PUSH	DE
	PUSH	BC
	PUSH	AF		;SAVE MACHINE STATE
	IN	A,(SIODPB)	;READ SIO DATA INPUT PORT
	AND	01111111B
	LD	HL,(SINVEC)	;GET SERIAL INPUT ROUTINE VECTOR

	ENDIF	;SIO.INTERRUPT

DSPTCH:	CALL	CALLHL		;CALL SUBROUTINE ADDRESSED BY H
	POP	AF
	POP	BC
	POP	DE
	POP	HL
	LD	SP,(SPSAVE)
	EI			;RE-ENABLE INTERRUPTS AND RETURN
	RETI
;
;
;	-- RX ERROR INTERRUPT SERVICE ROUTINE FOR SIO --
;
;	ARRIVE HERE IF RECIEVE INTERRUPT FROM FRAMING, OVERRUN
;	AND PARITY ERRORS. (PARITY CAN BE DISABLED)
;
SIOERR:
	IF	SIO.INTERRUPT

	LD	(SPSAVE),SP	;SAVE USER STACK POINTER AND
	LD	SP,TMPSTK+16	; SWITCH TO LOCAL STACK
	PUSH	AF
	CALL	SIOIN2		;CLEAR BAD CHARACTER FROM SIO
	LD	A,'G'-64
	CALL	SIOXMT		;OUTPUT A CTL-G AS A WARNING
	POP	AF
	LD	SP,(SPSAVE)

	ENDIF	;SIO.INTERRUPT

	IF	NOT SIO.INTERRUPT
SIOINT:
	ENDIF	;NOT SIO.INTERRUPT

	EI
	RETI
;
;
CALLHL:	JP	(HL)
;
;
;
;	POLLED MODE I/O ROUTINES FOR SIO CHANEL B
;
SIOST:	IN	A,(SIOCPB)	;GET SIO STATUS REGISTER
	AND	00000001B
	RET	Z		;ACC=0 IF NO DATA AVAILABLE
	LD	A,255
	RET
;
;
SIOIN:	CALL	SIOST		;TEST CONSOLE STATUS
	JR	Z,SIOIN	;LOOP UNTIL DATA IS RECIEVED
SIOIN2:	LD	A,00110000B	;RESET STATUS BITS IN SIO FOR
	OUT	(SIOCPB),A	; PARITY/OVERRUN/FRAMING ERRORS,
	IN	A,(SIODPB)	; THEN GET THE INPUT CHARACTER 
	AND	7FH
	RET
;
;
; We dont need null padding
SIOOUT:	;CP	' '		;TEST FOR CONTROL CHARACTERS
;	JR	NC,SIOXMT	;JUMP IF PRINTABLE CHARACTER
;	CALL	SIOXMT		; ELSE SEND CONTROL CHARACTER
;	LD	A,(NULLS)	; AND THEN SEND NULLS AS PADDING
;	INC	A		;GET NULL PAD COUNT AND FIX SO
;	JR	PAD1		; THAT COUNT=0 SENDS NO NULLS

;PAD:	PUSH	AF
;	XOR	A
;	CALL	SIOXMT		;OUTPUT A NULL TO THE SIO
;	POP	AF
;PAD1:	DEC	A
;	JR	NZ,PAD	;LOOP SENDING NULLS TO SIO
;	RET
;
;
SIOXMT:	PUSH	AF
SIOX1:	IN	A,(SIOCPB)
	AND	00000100B	;TEST TBE STATUS BIT
	JR	Z,SIOX1
	POP	AF
	OUT	(SIODPB),A	;OUTPUT DATA TO SIO
	RET
;
;
;************************************************
;*						*
;*	MEMORY-MAPPED CRT OUTPUT DRIVER		*
;*						*
;*	Russell Smith    18-August-1980		*
;*						*
;************************************************
;
;
CRTMEM	EQU	3000H	;Start of 3K CRT memory space
CRTBAS	EQU	CRTMEM SHR 8 ;STARTING PAGE# OF 3K CRT SPACE
CRTTOP	EQU	[CRTMEM+3072] SHR 8 ;ENDING PAGE# OF CRT SPACE
;
;
CRTOUT:
	PUSH	AF
	PUSH	HL
	PUSH	DE
	PUSH	BC
	RES	7,A
	LD	C,A
	DI		;KEEP THE WOLVES AWAY FOR A WHILE
	LD	(SPSAVE),SP
	LD	SP,TMPSTK+16 ;POINT SP TO TOP OF LOCAL STACK
	IN	A,(BITDAT)
	SET	7,A	    ;SELECT ROM/CRT MEMORY BANK
	OUT	(BITDAT),A
;
;FIRST REMOVE THE OLD CURSOR CHARACTER FROM THE SCREEN
;
	LD	HL,CHRSAV ;GET CHARACTER NOW OVERLAYED BY CURSOR
	LD	B,(HL)
	LD	HL,(CURSOR) ;LOAD HL WITH CURSOR POINTER
	LD	A,H
	AND	00001111B ;A LITTLE INSURANCE THAT HL CAN'T
	OR	CRTBAS	 ; EVER POINT OUTSIDE THE CRT MEMORY
	LD	H,A
	LD	(HL),B	;REMOVE CURSOR BY RESTORING CHARACTER
;
;	PROCESS CHARACTER PASSED IN C
;
	CALL	OUTCH
;
;NOW STORE A NEW CURSOR CHARACTER AT THE CURSOR LOCATION
;
	LD	A,(HL)	;GET CHARACTER AT NEW CURSOR LOCATION
	LD	(CHRSAV),A ;SAVE FOR NEXT TIME 'CRTOUT' IS CALLED
	CP	' '	;TEST IF CHARACTER IS A SPACE
	SET	7,A	;THEN TURN ON BIT 7 TO ENABLE BLINK
	JR	NZ,CRT2	;JUMP IF CHARACTER IS NON-BLANK
	LD	A,(CSRCHR) ;ELSE GET CHARACTER USED FOR CURSOR
CRT2:	LD	(HL),A	;STORE CHARACTER IN A AS CURSOR MARK
	LD	(CURSOR),HL ;SAVE HL AS CURSOR POINTER

	LD	SP,(SPSAVE)
	IN	A,(BITDAT)
	RES	7,A	;SWITCH BACK THE LOWER 16K OF RAM
	OUT	(BITDAT),A
	EI		;INTERRUPTS ARE SAFE AGAIN
	POP	BC
	POP	DE
	POP	HL
	POP	AF
	RET
;
;
;
OUTCH:	LD	DE,LEADIN
	LD	A,(DE)	;GET LEAD-IN SEQUENCE STATE
	OR	A
	JP	NZ,MULTI ;JUMP IF IN A LEAD-IN SEQUENCE
	LD	A,C	; ELSE PROCESS CHARACTER IN C
	CP	' '
	JR	C,CONTRL ;JUMP IF A CONTROL CHARACTER
DISPLA:	LD	(HL),C	; ELSE STORE DISPLAYABLE CHARACTER
	INC	HL	; AND ADVANCE POINTER TO NEXT COLUMN
	LD	A,L
	AND	01111111B ;EXTRACT COLUMN# FROM HL
	CP	80
	RET	C	;EXIT IF NOT PAST COLUMN 79
	CALL	RETURN	; ELSE DO AUTOMATIC CARRIAGE RETURN
	CALL	LFEED	; AND LINEFEED
	RET
;
;
;
CONTRL:	PUSH	HL
	LD	HL,CTLTAB ;SEARCH FOR CONTROL CHARACTER
	LD	BC,CTLSIZ/3 ;HANDLING SUBROUTINE IN TABLE
	CALL	SEARCH
	POP	HL
	RET	NZ	;EXIT IF NOT IMPLEMENTED
	PUSH	BC
	RET	;DO SNEAKY JUMP TO PRESERVE REGISTERS

CTLTAB:	DEFB	'_'-64
	DEFB	'^'-64
	DEFB	'['-64
	DEFB	'Z'-64
	DEFB	'X'-64
	DEFB	'Q'-64
	DEFB	'M'-64
	DEFB	'L'-64
	DEFB	'K'-64
	DEFB	'J'-64
	DEFB	'I'-64
	DEFB	'H'-64
	DEFB	'G'-64

	DEFW	BELL	;CTL-G IS THE BELL
	DEFW	BAKSPC	;CTL-H IS CURSOR LEFT
	DEFW	TAB	;CTL-I IS TAB
	DEFW	LFEED	;CTL-J IS CURSOR DOWN
	DEFW	UPCSR	;CTL-K IS CURSOR UP
	DEFW	FORSPC	;CTL-L IS CURSOR RIGHT
	DEFW	RETURN	;CTL-M IS CARRIAGE RETURN
	DEFW	CLREOS	;CTL-Q IS CLEAR TO END-OF-SCREEN
	DEFW	CLREOL	;CTL-X IS CLEAR TO END-OF-LINE
	DEFW	CLRSCN	;CTL-Z IS CLEAR SCREEN
	DEFW	ESCAPE	;CTL-[ IS ESCAPE
	DEFW	HOMEUP	;CTL-^ IS HOME UP
	DEFW	STUFF	;CTL-_ IS DISPLAY CONTROL CHARS

CTLSIZ	EQU	$-CTLTAB
;
;
ESCAPE:	LD	A,1
	LD	(DE),A	;SET LEAD-IN SEQUENCE STATE
	RET	     ; FOR XY CURSOR POSITIONING MODE
;
;
STUFF:	LD	A,4
	LD	(DE),A	;SET LEAD-IN SEQUENCE STATE
	RET		; FOR CONTROL CHAR OUTPUT MODE
;
;
BAKSPC	LD	A,L	;CHECK FOR LEFT MARGIN
	AND	01111111B
	RET	Z	;ABORT IF IN LEFTMOST COLUMN
	DEC	HL	;BACK UP CURSOR POINTER
	RET
;
;
FORSPC:	LD	A,L	;CHECK FOR RIGHTMOST COLUNM
	AND	01111111B
	CP	79
	RET	NC	;DO NOTHING IF ALREADY THERE
	INC	HL
	RET	    ;ELSE ADVANCE THE CURSOR POINTER
;
;
TAB:	LD	DE,8	;TABS ARE EVERY 8 COLUMNS
	LD	A,L	;GET COLUMN COMPONENT OF
	AND	01111000B ; PREVIOUS TAB POSITION
	ADD	A,E
	CP	80	;EXIT IF NEXT TAB COLUMN WOULD
	RET	NC	; BE PAST THE RIGHT MARGIN
	LD	A,L
	AND	11111000B ;ELSE INCREMENT THE CURSOR
	LD	L,A	  ; POINTER FOR REAL
	ADD	HL,DE
	RET
;
BELL:
	IF	NOT CACHE

	IN	A,(BITDAT)
	SET	4,A	  ;TOGGLE BIT 5 OF SYSTEM PIO TO
	OUT	(BITDAT),A ;TRIGGER BELL HARDWARE TO SOUND
;Now delay for 120msec
	PUSH	BC
	LD	B,120	;msec
LPX2:	LD	C,8AH
LPX3:	NOP
	NOP
	NOP
	DEC	C
	JR	NZ,LPX3
	DJNZ	LPX2
	POP	BC	;Done delay
	RES	4,A
	OUT	(BITDAT),A
	RET
	ENDIF	;NOT CACHE

	IF	CACHE

	IN	A,PIOBDAT	;We use GPPIO bit 7
	RES	7,A
	OUT	PIOBDAT,A
;Now delay for 120msec
	PUSH	BC
	LD	B,120
LPZ2:	LD	C,8AH
LPZ3:	NOP
	NOP
	NOP
	DEC	C
	JR	NZ,LPZ3
	DJNZ	LPZ2
	POP	BC
	SET	7,A
	OUT	PIOBDAT,A
	RET
	ENDIF	;CACHE
;
;
RETURN:	LD	A,L
	AND	10000000B
	LD	L,A	;MOVE CURSOR POINTER BACK
	RET		; TO START OF LINE
;
;
; Now clear the screen
CLRSCN:	IN	A,(BITDAT)
	SET	7,A
	OUT	(BITDAT),A	;Select CRT bank
	LD	HL,CRTMEM
	PUSH	HL
	LD	DE,CRTMEM+1
	LD	BC,24*128
	LD	(HL),' '
	LDIR		;FILL CRT MEMORY WITH SPACES
	POP	HL	;POINT TO HOME CURSOR POSITION
	LD	A,23
	LD	(BASE),A ;MAKE BASE LINE# BE 23 AND
	OUT	(SCROLL),A ; STORE IN SCROLL REGISTER
	RET
;
;
CLREOL:	PUSH	HL	;SAVE CURSOR POINTER
	LD	A,L
	AND	01111111B ;GET COLUMN# COMPONENT OF
	LD	C,A	; CURSOR POINTER INTO C
	LD	A,80	;CALCULATE HOW MANY CHARACTERS
	SUB	C	; REMAIN ON CURRENT LINE
	LD	B,A
	CALL	CLR	;CLEAR REST OF LINE @ HL
	POP	HL
	RET
;
;
CLREOS:	CALL	CLREOL	;CLEAR REMAINDER OF CURRENT ROW
	PUSH	HL
	LD	A,(BASE)
	LD	C,A	;COPY BASE SCREEN ROW# TO C
CLRS1:	LD	A,L
	RLA
	LD	A,H
	RLA		;GET ROW# COMPONENT OF HL INTO A
	AND	00011111B
	CP	C  ;SEE IF HL IS AT BOTTOM ROW OF SCREEN
	JR	Z,CLRS2	; AND LEAVE CLEAR LOOP IF SO
	CALL	DNCSR	;ELSE POINT HL TO NEXT ROW DOWN
	CALL	CLRLIN	; AND FILL THAT LINE WITH SPACES
	JR	CLRS1

CLRS2:	POP	HL	;RESTORE ORIGINAL CURSOR POINTER
	RET
;
;
UPCSR:	LD	DE,-128	;SUBTRACT 1 FROM ROW# COMPONENT
	ADD	HL,DE	; OF CURSOR POINTER IN HL
	LD	A,H
	CP	CRTBAS	;CHECK FOR UNDERFLOW OF POINTER
	RET	NC
	LD	H,CRTTOP-1 ;WRAP CURSOR AROUND MODULO 3K
	RET
;
;
DNCSR:	LD	DE,128	;ADD 1 TO ROW# COMPONENT
	ADD	HL,DE	;OF CURSOR POINTER IN HL
	LD	A,H
	CP	CRTTOP	;CHECK FOR OVERFLOW OF POINTER
	RET	C
	LD	H,CRTBAS ;RESET POINTER MODULO 128*24
	RET
;
;
;
LFEED:	LD	A,L
	RLA
	LD	A,H
	RLA		;EXTRACT ROW# COMPONENT OF HL
	AND	00011111B
	LD	C,A	;COPY ROW# INTO C FOR SCROLL TEST
	CALL	DNCSR	;MOVE CURSOR TO NEXT ROW DOWN
	LD	A,(BASE) ;TEST IF CURSOR WAS ON BOTTOM ROW
	CP	C	; OF SCREEN BEFORE MOVING DOWN
	RET	NZ	;EXIT IF NOT AT BOTTOM

	PUSH	HL	;ELSE PREP TO SCROLL SCREEN UP
	CALL	CLRLIN	;FILL NEW BOTTOM LINE WITH SPACES
	ADD	HL,HL
	LD	A,H	;GET ROW# COMPONENT OF HL INTO A
	AND	00011111B
	LD	(BASE),A ;STORE NEW BASE LINE#
	OUT	(SCROLL),A ;NOW SCROLL UP NEW BLANK BOTTOM LINE
	POP	HL
	RET
;
;
CLRLIN:	LD	A,L
	AND	10000000B ;POINT HL TO FIRST COLUMN OF ROW
	LD	L,A
	LD	B,80
CLR:	LD	(HL),' ' ;STORE ASCII SPACES AT ADDRESS IN HL
	INC	HL	 ; AND INCREMENT HL
	DJNZ	CLR	 ;REPEAT NUMBER OF TIMES GIVEN BY B
	RET
;
;
HOMEUP:	LD	C,' ' ;FAKE-OUT CURSOR ADDRESSING ROUTINE
	JR	SETROW	; TO DO HOMEUP ALMOST FOR FREE
;
;
MULTI:	EX	DE,HL ;UNCONDITIONALLY RESET THE LEAD-IN
	LD	(HL),0 ; STATE TO ZERO BEFORE GOING ON
	EX	DE,HL
	CP	1
	JR	NZ,M2TST
SETXY:	LD	A,C	;GET SECOND CHAR OF SEQUENCE
	CP	'='
	RET	NZ	;ABORT SEQUENCE IF NOT '='
	LD	A,2
	LD	(DE),A	;MAKE LEADIN=2 NEXT TIME
	RET

M2TST:	CP	2
	JR	NZ,M3TST
	LD	A,3
	LD	(DE),A	;MAKE LEADIN=3 NEXT TIME
SETROW:	LD	A,(BASE) ;ARRIVE HERE ON THIRD CHARACTER
	ADD	A,C	 ; OF ESC,'=',ROW,COL SEQUENCE
	SUB	' '-1
SETR2:	SUB	24
	JR	NC,SETR2 ;MAKE SURE ROW# IS BETWEEN 0 AND 23
	ADD	A,24
	OR	CRTMEM SHR 7 ;MERGE IN MSB'S OF CRT MEMORY
	LD	H,A
	LD	L,0
	SRL	H
	RR	L
	RET

M3TST:	CP	3
	JR	NZ,M4TST
SETCOL:	LD	A,C		;ARRIVE HERE ON FOURTH CHARACTER
	SUB	' '		; OF ESC,'=',ROW,COL SEQUENCE
SETC2:	SUB	80
	JR	NC,SETC2	;MAKE SURE COL# IS BETWEEN 0 AND 79
	ADD	A,80
	OR	L		;MERGE IN COL# WITH L
	LD	L,A
	RET

M4TST:	CALL	DISPLA		;DISPLAY THE CONTROL CHARACTER
	RET			; PASSED IN C
;
;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	FILTER FOR CDOS SYSTEM CALLS
;	LAST CHANGE	12:Mar:81
;
;	Written by Steve Hope, Trevor Marshall,
;		Wait-Aid, Western Australia
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
FILTER:
	PUSH	AF
	LD	A,C
	CP	80H		;CP/M 0-80H
	JR	GE,CDOS		;CHK IF CDOS COMPATIBLE
	CP	2		;WRITE CONSOL NEEDS EXTRA CARE,A must be kept
	JR	Z,SYSC2
	CP	11	;Console status must return all
	JR	Z,SYSC11
	CP	15	;Open file must zero (DE)+32 (current record)
	CALL	Z,SYSC15
	POP	AF		;FIX STACK
	LD	(PARAM1),HL	;save temporarily
	LD	(PARAM2),DE
	LD	(PARAM3),BC
	CALL	BDOS+0BH	;Usually E011, Go execute BDOS
	LD	BC,(PARAM3)
	LD	DE,(PARAM2)
	PUSH	AF
	LD	A,C		;Check functions which return values in HL
	CP	27	;Max 1.4 FN
	JR	GE,RET67 ;All higher return HL
	CP	24	;login code
	JR	EQ,RET67
	CP	12	;Get version
	JR	EQ,RET67
	LD	HL,(PARAM1)
RET67:
	POP	AF
	RET
;
SYSC2:
	LD	(SPSAV),SP	;the CP/M DIR command always bombs
	LD	SP,STACK	;due to stack overflow unless
	LD	A,E		;we give it some help
	CALL	CONOUT	;Destroys nothing
	LD	SP,(SPSAV)
	POP	AF
	RET
;
SYSC11: POP	AF
	CALL	CPMST
	OR	A
	RET	Z	;Don't alter HL unless we have to
	LD	L,A	; required for CP/M compatibility
	RET
;
SYSC15: PUSH	HL	;This is required
	LD	HL,32	; for CDOS
	ADD	HL,DE
	LD	(HL),0
	POP	HL
	RET	;thence to BDOS
;
CDOS:	
	CP	80H		;READ NO ECHO
	JR	Z,RNEC
	CP	82H		;Set CTL-C abort (it doesn't)
	JR	Z,SYSC82
	CP	86H		;FORMAT NAME TO FCB
	JR	Z,FFCB
;	CP	8EH		;CRT FUNCTION (XY POS)
;	JP	Z,CRT
	CP	89H		;MULTIPLY INTEGERS
	JP	Z,MULT
	CP	8AH		;DIVIDE INTEGERS
	JP	Z,DIVIDE
	CP	8BH
	JP	Z,HOME		;Home selected disk
	CP	8DH		;Get version
	JP	Z,GETVERSION
	CP	90H		;Read date
	JR	Z,READ.DATE
	CP	92H		;Read time of day
	JR	Z,READ.DATE	;Return zero, same as 90H
	CP	96H		;Turn motors off
	JR	Z,MOTORS.OFF
	POP	AF		;FIX STACK
	JP	CALLERR	;ELSE ERROR FOR NOW
;
RNEC:				;READ NO ECHO
	POP	AF		;FIX STACK
	JP	CPMIN	;And return with char in acc.
;
READ.DATE: POP	AF
	XOR	A	; A=0
	LD	C,A
	LD	B,A	;BC=0
	RET		;***Done ***
;
MOTORS.OFF: IN	A,(BITDAT)
	OR	A,01000100B ;Motors off, drive deselected
	OUT	(BITDAT),A
SYSC82:	POP	AF
	RET

;;;
FFCB:	
	POP	AF		;FIX STACK
	PUSH	DE		;SAVE DE
	LD	B,(HL)		;GET FIRST CHAR
	INC	HL
	LD	A,(HL)
	CP	':'		;CHK IF DRIVE
	JR	NZ,NOCOL
	INC	HL		;PAST COL
	LD	A,7
	AND	B		;DRV #
	JR	FBYTE
NOCOL:	DEC	HL
	XOR	A		;DEFAULT DRV
FBYTE:	
	LD	(DE),A
	INC	DE
	LD	B,8
;
NAME1:
	LD	A,(HL)
	CP	'*'
	JR	EQ,FILLWILD
	CP	21H
	JR	C,FILLL1
	INC	HL
	CP	'.'
	JR	Z,FILLL1
	LD	(DE),A
	INC	DE
	DJNZ,NAME1
	JR	EX1
FILLWILD:
	LD	A,'?'
	LD	(DE),A
	INC	DE
	DJNZ	FILLWILD
	JR	EX1
FILLL1:	
	LD	A,' '
	LD	(DE),A
	INC	DE
	DJNZ,FILLL1
EX1:
;;;;;;
	LD	A,'.'
	CP	(HL)
	JR	NZ,EXT
	INC	HL
EXT:	LD	B,3
NAME2:	
	LD	A,(HL)
	CP	'*'
	JR	EQ,WILDEXT
	CP	21H
	JR	C,FILLL2
	INC	HL
	CP	'.'
	JR	Z,FILLL2
	LD	(DE),A
	INC	DE
	DJNZ,NAME2
	JR	EX2
WILDEXT: LD	A,'?'
	LD	(DE),A
	INC	DE
	DJNZ	WILDEXT
	JR	EX2
FILLL2:	
	LD	A,' '
	LD	(DE),A
	INC	DE
	DJNZ,FILLL2
EX2: ;Done, restore ready for exit
	XOR	A
	LD	(DE),A
	POP	DE
	RET	


;CRT:	
;	POP	AF		;FIX STACK
;	XOR	A
;	CP	E		;CANT HANDLE ALL
;	JR	NZ,CRT3
;	LD	A,1AH	;Clearscreen char
;	JP	CRTOUT	;Neglect all other special fns
;CRT3:	PUSH	DE		;SAVE DE
;	CALL	PNEXT
;	DB	1BH,'=',EOT
;	POP	DE		;RECOVER DE
;	PUSH	DE		;SAVE AGAIN
;	LD	A,1FH
;	ADD	E		;ROW
;	CALL	CONOUT
;	POP	DE
;	PUSH	DE
;	LD	A,1FH
;	ADD	D		;COL
;	CALL	CONOUT
;	POP	DE
;	RET	
;;;;;;;;;;;;;;;;;;;;;;;;;;

MULT:				;DE=HL*DE
	POP	AF		;FIX STACK
	PUSH	BC		;SAVE REGS
	PUSH	HL
	LD	L,16
	LD	BC,0
MLOOP:
	EX	(SP),HL
	LD	A,1
	AND	A,E
	JR	Z,M1
	PUSH	HL
	ADD	HL,BC
	EX	(SP),HL
	POP	BC
M1:
	RR	B
	RR	C
	RR	D
	RR	E
	EX	(SP),HL
	DEC	L
	JR	NZ,MLOOP
	POP	HL
	POP	BC
	RET


DIVIDE:				;HL=HL/DE , DE=REMAINDER
	POP	AF		;FIX STACK
	LD	A,D
	OR	A,E
	RET	Z		;DIVIDE BY 0
	LD	A,D
	CPL
	LD	D,A
	LD	A,E
	CPL
	LD	E,A
	INC	DE		;2'S COMP
	PUSH	BC
	LD	B,H
	LD	C,L
	LD	HL,0
	LD	A,17
DLOOP:
	PUSH	HL
	ADD	HL,DE
	JR	NC,D1
	EX	(SP),HL
D1:
	POP	HL
	RL	C
	RL	B
	RL	L
	RL	H
	DEC	A
	JR	NZ,DLOOP
	OR	A,A
	RR	H
	RR	L
	EX	DE,HL
	LD	H,B
	LD	L,C
	POP	BC
	RET

;;;;;;;;

CALLERR:
	CALL	PNEXT
	DEFB	CR,LF,'Illegal system call ',EOT
	LD	A,C
	CALL	PUT2HEX
	CALL	PNEXT
	DB	',PC=',EOT
	POP	HL		;GET CALL ADDR
	PUSH	HL		;REt addr back on stack
	DEC	HL		;start
	DEC	HL
	DEC	HL
;NOW PRINT ADDR OF ILLEGAL CALL
	CALL	PUT4HS	;Print calling addres
	XOR	A	;Set up return variable values=0
	LD	BC,0
	LD	DE,0
	LD	HL,0
	RET		;Hope program will not bomb
;
GETVERSION: POP	AF
	LD	BC,0107H ;Version 1.07
	RET
;
;
;
;************************************************
;*						*
;* DISK INPUT/OUTPUT DRIVER SUBROUTINE PACKAGE	*
;* FOR WESTERN DIGITAL 1771 DISK CONTROLLER	*
;*						*
;* bullet-proof error recovery added 12-APR-80	*
;*						*
;************************************************
;
;
;EQUATES FOR DISK CONTROLLER PORTS AND COMMAND CODES
;
STSREG	EQU	WD1771+0 ;STATUS REGISTER
CMDREG	EQU	WD1771+0 ;COMMAND REGISTER
TRKREG	EQU	WD1771+1 ;TRACK REGISTER
SECREG	EQU	WD1771+2 ;SECTOR REGISTER
DATREG	EQU	WD1771+3 ;DATA REGISTER
;
RDCMD	EQU	10001000B ;READ COMMAND
WRTCMD	EQU	10101000B ;WRITE COMMAND
SKCMD	EQU	00011100B ;SEEK COMMAND
FINCMD	EQU	11010000B ;FORCE INTR COMMAND
RSTCMD	EQU	00001100B ;RESTORE COMMAND
HLOAD	EQU	00000100B ;RD/WRT HEAD LOAD ENABLE
;
RET	EQU	0C9H	;SUBROUTINE RETURN INSTR OPCODE
NMIVEC	EQU	0066H	;THE NON-MASKABLE INTERRUPT IS
		;USED FOR DATA SYNCRONIZATION BETWEEN
		;THE Z-80 AND 1771 DISK CONTROLLER
;
;
;
SETSEC:	LD	HL,SECTOR
	LD	(HL),C
	XOR	A ;To prevent errors being notified
	RET
;
SETDMA: LD	(IOPTR),BC
	XOR	A
	RET
;
;
SECTRAN: EX	DE,HL	;ADD TRANSLATION TABLE ADDRESS
	ADD	HL,BC	; PASSED IN DE TO SECTOR# IN BC
	LD	L,(HL)
	LD	H,0	;LOOKUP PHYSICAL SECTOR NUMBER
	XOR	A
	RET		; AND RETURN IT IN HL
;
;
SELECT:	LD	HL,0	;In case of error exit	
	LD	A,C	;GET UNIT# PASSED IN C AND
	CP	2	; CHECK FOR MAXIMUM VALID#
	RET	NC	;ERROR IF NUMBER > 1
;
	LD	(SAVE.DRIVE),A ;Stash Unit# for DOSELECT later
; check that it is logged
	LD	HL,TRKTAB ;Calculate trk ptr of requested drive
	LD	A,L
	ADD	C
	LD	L,A	;Now pointing at the modified byte
	LD	A,(HL)	;Has this drive been logged on?
	CP	0FFH
	LD	A,C	;Get new dsk# to A ready for RET21
	JR	NE,RET21 ;Only a number of FF indicates not logged
			 ;so if anything else make no hardware changes yet
			 ;If the drive is not logged we had better do so, as
			 ; BDOS expects DPB info on its size
	CALL	DOSELECT
	RET
;
;
DOSELECT: 
	LD	A,(SAVE.DRIVE)
	LD	C,A
	LD	HL,UNIT	;POINT HL TO DRIVE SELECT DATA
	LD	A,(HL)	;LOAD A WITH CURRENT UNIT#
	LD	(HL),C	; AND STORE NEW UNIT# FROM C
;Check if drive is already current
	LD	E,A	;Save current #
	SUB	A,C
	JR	Z,RET20 ;If so, exit
;
 	LD	D,C	;Save new drive
	IN	A,(BITDAT)
	AND	11011000B ;MERGE IN NEW DRIVE UNIT# & side 0
	OR	C	; IN PLACE OF THE CURRENT ONE
	OUT	(BITDAT),A ; TO SELECT THE NEW DISK DRIVE
;Delay turnon until an operation is actually requested
;	CALL	TURNON	;After selecting drive
;	BIT	7,A	 ;TEST DRIVE NOT READY BIT
;	JR	Z,SEL2	; AND CONTINUE IF ITS READY
;	LD	HL,0	;for BDOS
;	LD	A,10000000B ;else RETURN DRIVE-NOT-READY STATUS
;	OR	A
;	RET
SEL2:
;We will store the old track number and RECLEN for later
	LD	C,D	;retrieve new drive
	LD	A,E	;retrieve original UNIT byte
	CP	255	;TEST IF NO DRIVE HAS BEEN SELECTED
; Else store last drive's parameters
	JR	Z,SEL3	;YET AND SKIP NEXT SEGMENT IF SO
	INC	HL	;POINT TO HEAD POSITION TABLE
	ADD	A,L	; AND ADD IN OLD UNIT# AS INDEX
	LD	L,A
	LD	A,(TRACK) ;Get current track position
	LD	(HL),A	; AND STORE IN TABLE @ HL
	LD	A,TRKTAB.LENGTH ;RECLENTAB next
	ADD	A,L
	LD	L,A
	LD	A,(RECLEN)
	LD	(HL),A	;Store it in reclen table
SEL3:	LD	HL,RECLENTAB
	LD	A,L
	ADD	A,C	;INDEX INTO TABLE TO GET
	LD	L,A	; RECLEN OF NEW DRIVE
	LD	A,(HL)
	LD	(RECLEN),A
	LD	A,-TRKTAB.LENGTH ;Now get select data
	ADD	A,L
	LD	L,A
	LD	A,(HL)
	CP	255	;TEST IF NEW DRIVE HAS EVER BEEN
	JR	NE,NXT111 ; SELECTED AND
	CALL	LOGIN	;Do a LOGIN if not
	LD	HL,0	;for an error exit
	RET	NZ
;If LOGIN fails leave drive logged off,HL=0
NXT111:	LD	(TRACK),A ;Success,store the current head position
;Now wait for a while to allow time for head load solenoid
	IF	IS5MHZ
	LD	B,8	;Loop four times
	ELSE
	LD	B,4
	ENDIF	;IS5MHZ

LP124:	IN	A,CTC3	;Get current CTC3 value
	LD	C,A	;Save it
LP123:	IN	A,CTC3
	CP	C	;Loop until it has changed
	JR	NZ,LP123 ;The first may be a short cycle
	DJNZ	LP124	;the rest will be a full 5.5 msec at 5MHz
;
RET20:	LD	A,(UNIT) ;get current unit
RET21:	LD	H,0
	LD	L,A	;Calculate DPHB addr
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL	;MULTIPLY UNIT# BY 16
	LD	DE,DPBASE
	ADD	HL,DE	;ADD START ADDRESS OF DHP BLOCK
	XOR	A	;No errors, HL points to DPBASE
	RET
;
;
;
; We log the disks in under HOME
; Home aways in S/D
;
HOME:
	CALL	DOSELECT
	RET	NZ
;
	IN	A,(BITDAT)
	SET	3,A	;select S/D
	RES	5,A	;select side 0
	OUT	(BITDAT),A
;
	CALL	TURNON	;CLEAR DISK CONTROLLER
	AND	10000001B ;Mask statuses
	RET	NZ
RESTOR:	LD	A,RSTCMD ;LOAD B WITH A RESTORE COMMAND
	CALL	CMDOUT
	AND	10011000B ;Mask off TRK 0 status
	LD	(TRACK),A ;SET TRACK# IN MEM TO ZERO (A is 0)
	RET	;If any errors trk# is wrong
;
;LOGIN checks the disk density
;
LOGIN:	CALL	HOME
	RET	NZ

;Now log on the disk to see if it is double or single density
	INC	A	;read sector 1
	LD	(SECTOR),A
;
	LD	HL,(IOPTR)
	LD	(OLDPTR),HL
	LD	HL,LOGBUF ;default dsk buffer
	LD	(IOPTR),HL
;
	LD	A,80H	;Bit 7 set to indicate LOGread
	LD	(RECLEN),A ;Default S/D
	CALL	READ
;
	LD	HL,(OLDPTR) ;Restore previos DMA addr
	LD	(IOPTR),HL
	RET	NZ	;to DSKERR
;
	LD	HL,LOGBUF ;look for 'DS' on the label
	LD	A,'D'
	CP	(HL)
	JR	NE,SINGLED
	INC	HL
	LD	A,'S'
	CP	(HL)
	JR	NE,SINGLED
; If we are here then it is at least double sided
	INC	HL
	LD	A,'D'
	CP	(HL)
	JR	NE,DOUBLES
	INC	HL
	CP	(HL)
	JR	NE,DOUBLES
;Here it must be double sided double density
	LD	A,11B	;128 bytes/sector +D/D + D/S
	JR	SD2
DOUBLES: LD	A,10B	;128 bytes/sector +D/S
	JR	SD2
; Load the sector length/2 for read/writes
SINGLED: LD	A,00B ;128 bytes/sector
SD2:	LD	(RECLEN),A
;Now we must change the Disk Parameter Blocks
	LD	HL,DPB0	;The D/D disk parameter block
	LD	BC,XLT0 ;The D/D interleave table
	BIT	0,A	;Test D/D flag
	JR	NZ,SETPATCH ;If D/D then use those values
	LD	HL,DPB1	;Else load D/S values
	LD	BC,XLT1
	BIT	1,A	;Test D/S flag
	JR	NZ,SETPATCH
	LD	HL,DPB2	;Else load S/D DPBlk
SETPATCH: ;Now have to calculate the patch address to use
	LD	A,(UNIT)
	OR	A	;Is it A:? (DPE0)
	JR	NZ,NEXTPATCH ;No, patch DPE1
	LD	(PATCH1),BC
	LD	(PATCH2),HL
	JR	DONEPATCH
NEXTPATCH: LD	(PATCH3),BC
	LD	(PATCH4),HL	
DONEPATCH:
	XOR	A
	RET
;
;
;
SEEK:
	PUSH	BC	;Save trk #
	CALL	DOSELECT
	POP	BC
	RET	NZ

	CALL	TURNON	;CLEAR DISK CONTROLLER
	BIT	7,A
	RET	NZ	;EXIT IF DRIVE NOT READY
	LD	A,(TRACK) ;Get current track#
	OUT	TRKREG,A ;to the 1771
	LD	HL,RECLEN ;Dont do next if D/D
	BIT	1,(HL)	;is it D/D ?
	SCF		;clr Carry
	CCF		;ready to select side 0
	JR	Z,SK1	;skip track/side calculation if S/D
	RR	C	;now carry is side#, track is in C
;PA5 is side select
SK1:	IN	A,(BITDAT)	;get side select mask
	RES	5,A	;default side 0 again
	LD	B,0	;use B as carry mask
	RR	B	;carry into b7 of B
	RR	B
	RR	B	;now in b5 of B
 	OR	A,B	;OR BITDAT with B, the side select mask
	LD	B,A	;save BITDAT (A) to B
;Are we reading a single or double density track?
	SET	3,B	;set up BITDAT mask for S/D
	BIT	5,B	;test side bit for side 1
	JR	NZ,SK2	;so that trk 0, side 0 is always S/D
	LD	A,C
	OR	A	;On side 0, is the trk # also = 0 ?
	JR	Z,NSKP	;yes, always make trk 0, side 0, S/D
SK2:	BIT	0,(HL)	;(RECLEN) (Z=S/D)
;	SET	3,B	;DD* bit is reset ready for single density
	JR	Z,NSKP	; exit calculation if single density disk
	RES	3,B	;here it must be DD
NSKP:	LD	A,B	;(get BITDAT mask back to A ready for)
	OUT	(BITDAT),A

	LD	A,(TRACK) ;GET old TRACK#
	SUB	C	;Is new trk the same as the last?
	RET	Z	;Yes,return with 0 in A

	LD	A,C	;get new trk#
	LD	(TRACK),A ; STORE TRACK# FOR SEEK
	OUT	(DATREG),A ;OUTPUT TRACK # TO 1771
	LD	A,SKCMD	;LOAD A WITH A SEEK COMMAND AND
	CALL	CMDOUT
	AND	10011000B ;MASK TO READY,SEEK AND CRC ERROR
	PUSH	AF
;
;Now a short delay to allow head to settle
	IF	IS5MHZ
	LD	B,6	;Loop 6 times at 5MHz
	ELSE
	LD	B,3
	ENDIF	;IS5MHZ

LP24:	IN	A,CTC3	;Get current CTC3 value
	LD	C,A	;Save it
LP23:	IN	A,CTC3
	CP	C	;Loop until it has changed
	JR	NZ,LP23 ;The first loop may be a short cycle
	DJNZ	LP24	;the rest will be a full 5.5 msec at 5MHz
;
	POP	AF
	RET		;RETURN FINAL SEEK STATUS IN A
;
;
;We wont check if drive ready here, error handler will fix any exceptions
;Famous last words, system lost speed if following call to TURNON was omitted==
WRITE:	PUSH	HL	;HL is required for SELECT errors
	CALL	TURNON
; If disk is write protected prompt now
	BIT	6,A
	JP	Z,NXT571 ;Z = OK
	CALL	PNEXT	;Print to console
	DB	CR,LF,7,'Disk is write protected',EOT
	CALL	CPMIN	;Get any key to acknowledge message
	CALL	TURNON	;Operator may be asleep
NXT571:	AND	11000001B
	POP	HL
	RET	NZ	;EXIT IF DRIVE NOT READY or still protected
	LD	B,WRTCMD
	JR	RDWRT

READ:
	PUSH	HL
	CALL	TURNON
	AND	10000001B
	POP	HL
	RET	NZ	;EXIT IF DRIVE NOT READY
	LD	B,RDCMD
RDWRT:	LD	HL,CMDTYP
	LD	(HL),B	;SAVE READ/WRITE COMMAND BYTE
	INC	HL
	LD	(HL),4	;SET DISK OPERATION RE-TRY COUNT
RW1:	CALL	SETUPNMI
;Are we reading a single or double density track?
	LD	B,80H	;set up B for S/D
SK4:			;B=NUMBER OF BYTES/SECTOR
	LD	C,DATREG ;C=1771 DATA REGISTER PORT#
	LD	A,(SECTOR) ;GET SECTOR NUMBER
	OUT	(SECREG),A ;OUTPUT SECTOR# TO 1771
	CALL	FORCE	 ;ISSUE A FORCE INTERRUPT COMMAND
	BIT	5,A	 ; TO TEST CURRENT HEAD LOAD STATUS
	LD	A,(CMDTYP) ;GET READ OR WRITE COMMAND BYTE
	JR	NZ,RW2	;JUMP IF HEAD IS ALREADY LOADED
	OR	HLOAD	; ELSE MERGE IN HLD BIT
RW2:
	LD	HL,RECLEN
	BIT	7,(HL)

	LD	HL,(IOPTR) ;Set up DMA pointer
	OUT	CMDREG,A ;START the 1771 reading/writing

	JP	NZ,LOGLOOP	;Reclen test above

	BIT	5,A	;TEST IF COMMAND IS A READ OR WRITE
	JR	NZ,WLOOP ; AND JUMP TO THE CORRECT LOOP

RLOOP:	HALT
	INI		
	JP	NZ,RLOOP
RW6:	CALL	IRQ2
	AND	10011100B ;MASK OFF TO READY, NOT FOUND, CRC
	JR	RW3	; AND LOST DATA STATUS BITS
;
LOGLOOP: LD	B,7AH	;Bytes to discard
LPZY2:	HALT
	IN	A,(C)
	DJNZ	LPZY2 ;Speed not important as only S/D
	LD	B,6
LPZY3:	HALT	;Now collect the data bytes
	INI
	JP	NZ,LPZY3
	JR	RW6
;
WLOOP:	HALT
	OUTI
	JP	NZ,WLOOP
	CALL	IRQ2
	AND	11111100B ;MASK OFF AS ABOVE PLUS WRITE FAULT
RW3:	RET	Z	;RETURN IF NO DISK I/O ERRORS
	LD	HL,RETRY
	DEC	(HL)	;DECREMENT RE-TRY COUNT AND
	JR	NZ,RW1	;Execute command again if not=0
	OR	A
	RET		;ELSE RETURN 1771 ERROR STATUS

;
;
;Forcing a D1 interrupt here hangs (197X app p3) 
CMDOUT: OUT	CMDREG,A ;Output the command
	CALL	SETUPNMI
IRQ2:	HALT
	CALL	RESTORENMI
	IN	A,STSREG
	RET
;
FORCE:	LD	A,FINCMD
	OUT	CMDREG,A
LP7:	IN	A,STSREG
	BIT	0,A	;Still busy?
	JR	NZ,LP7
	RET
;
SETUPNMI: DI
	LD	HL,NMIVEC
	LD	D,(HL)
	LD	A,0C9H
	LD	(HL),A
	RET
;
RESTORENMI: LD	HL,NMIVEC
	LD	(HL),D
	EI
	RET
;
TURNON:	
	LD	A,30
	LD	(MOTOR),A ;RE-LOAD THE MOTOR TURN-OFF TIMER
;Just testing the motors status here is no good if 
;  a new drive has just been selected
; Test to see if new drive is ready here
	CALL	FORCE
;	AND	11011001B	;do in main routines
	BIT	7,A	;Ready?
	RET	Z	; AND EXIT IF STILL TURNED ON
	IN	A,(BITDAT)
	AND	11111011B ;ELSE RE-ENABLE THE DRIVE SELECTS
	OUT	(BITDAT),A ; AND ACTIVATE THE MOTOR RELAY
	PUSH	BC
	LD	B,0 ;SET READY LOOP MAX TIMEOUT
;
TURN2:	CALL	WAIT	;WAIT 1/93 SECOND AND TEST READY
	RLA	;Test the ready status bit
	JR	NC,TURN3 ;EXIT LOOP IF DRIVE READY
	DJNZ	TURN2	; ELSE TRY AGAIN UP TO 256 TIMES
TURN3:	LD	B,4
TURN4:	CALL	WAIT	;GIVE ABOUT 1/20 SECOND MORE DELAY
	DJNZ	TURN4	;to ensure no CRC errors
;	CALL	FORCE	;Done in WAIT
;	AND	11011001B ;do in main routines
	POP	BC
	RET
;
;
WAIT:	IN	A,(CTC3) ;GET CURRENT CTC3 COUNT VALUE
	LD	C,A
WAIT2:	IN	A,(CTC3)
	CP	C	;TEST IF CTC3 HAS CHANGED BY 1 COUNT
	JR	Z,WAIT2	; AND LOOP UNTIL IT CHANGES
	JR	FORCE	; THEN GO TEST DRIVE READY STATUS
;
;
;****************************************
;*					*
;* DISK I/O BUFFER, PARAMETER TABLES	*
;*					*
;****************************************
;
DPBASE	EQU	$	;BASE OF DISK PARAMETER BLOCKS
;Use two Single Sided Single Density Blocks
;Which can be patched as required
DPE0:
PATCH1:	DW	XLT1,0000H	;TRANSLATE TABLE
	DW	0000H,0000H	;SCRATCH AREA
	DW	DIRBUF
PATCH2:	DW	DPB2	;DIR BUFF,PARM BLOCK
	DW	CSV0,ALV0	;CHECK, ALLOC VECTORS (Use largest ones always)
DPE1:
PATCH3:	DW	XLT1,0000H	;TRANSLATE TABLE
	DW	0000H,0000H	;SCRATCH AREA
	DW	DIRBUF
PATCH4:	DW	DPB2	;DIR BUFF,PARM BLOCK
	DW	CSV1,ALV1	;CHECK, ALLOC VECTORS
;
; DOUBLE DENSITY DOUBLE SIDED
;	DISKDEF	0,1,34H,4,4096,(77-2)*2*52/32,192,192,2
DPB0	EQU	$		;DISK PARM BLOCK
	DW	52		;SEC PER TRACK
	DB	5		;BLOCK SHIFT
	DB	31		;BLOCK MASK
	DB	0		;EXTNT MASK
	DW	242		;DISK SIZE-1
	DW	191		;DIRECTORY MAX
	DB	192		;ALLOC0
	DB	0		;ALLOC1
	DW	48		;CHECK SIZE
	DW	2		;OFFSET
;
;SINGLE DENSITY DOUBLE SIDED
;	DISKDEF 1,1,1AH,6,2048,(77-2)*2*1AH/16,128,128,2
DPB1	EQU	$		;DISK PARM BLOCK
	DW	26		;SEC PER TRACK
	DB	4		;BLOCK SHIFT
	DB	15		;BLOCK MASK
	DB	0		;EXTNT MASK
	DW	242		;DISK SIZE-1
	DW	127		;DIRECTORY MAX
 	DB	192		;ALLOC0
	DB	0		;ALLOC1
	DW	32		;CHECK SIZE
	DW	2		;OFFSET
;
;SINGLE DENSITY SINGLE SIDED
;	DISKDEF 1,1,1AH,6,1024,(77-2)*1AH/8,64,64,2
DPB2	EQU	$		;DISK PARM BLOCK
	DW	26		;SEC PER TRACK
	DB	3		;BLOCK SHIFT
	DB	7		;BLOCK MASK
	DB	0		;EXTNT MASK
	DW	242		;DISK SIZE-1
	DW	63		;DIRECTORY MAX
	DB	192		;ALLOC0
	DB	0		;ALLOC1
	DW	16		;CHECK SIZE
	DW	2		;OFFSET
;
XLT0:		;TRANSLATE TABLE (Double Density)
	DB	1,5,9,13,17,21,25,29,33,37,41,45,49,2,6,10,14,18,22,26,30,34
	DB	38,42,46,50,3,7,11,15,19,23,27,31,35,39,43,47,51,4,8,12,16,20
	DB	24,28,32,36,40,44,48,52		
;
XLT1:		;TRANSLATE TABLE (IBM)
	DB 1,7,13,19,25,5,11,17,23,3,9,15,21,2,8,14,20,26,6,12,18,24,4,10,16,22
;
DIRBUF:	DS	128	;DIRECTORY ACCESS BUFFER
;
ALLOCATION.VECTORS: ;Must be zeroed on Warm Boot
ALV0:	DS	31	;Double Sided, Double Density
CSV0:	DS	48
;
ALV1:	DS	31
CSV1:	DS	48
VECTORS.LENTH: EQU $-ALLOCATION.VECTORS
;
;
;These are smaller, and not required
;ALV:	DS	31	;Double Sided Single Density 
;CSV:	DS	16
;ALV:	DS	31	;Single Sided Single Density same as DSSD
;CSV:	DS	16
;
;
LOGBUF:	DS	6	;For logging in disks
;
SAVE.DRIVE: DS	1
;
END.ROM: EQU	$	!Ignore error
;
;
;********************************************************
;*							*
;*	STORAGE ALLOCATION FOR 256 BYTE FIXED RAM	*
;*							*
;********************************************************
;
	ORG	0FF00H	;Top page of RAM
:
MEM:	EQU	$
;

VECTAB	EQU	$	;INTERRUPT VECTOR TABLE STARTS HERE
SIOVEC:	DEFS	16	;SPACE FOR 8 VECTORS FOR SIO
CTCVEC:	DEFS	8	;SPACE FOR 4 VECTORS FOR CTC
SYSVEC:	DEFS	4	;SPACE FOR 2 VECTORS FOR SYSTEM PIO
GENVEC:	DEFS	4	;SPACE FOR 2 VECTORS FOR GENERAL PIO
;
;
;
;	DISK I/O DRIVER VARIABLES
; Be careful not to straddle a Page Boundary with these
UNIT:	DEFS	1	;CURRENTLY SELECTED DISK#
TRKTAB:	DEFS	2	;2 DRIVE HEAD POSITION TABLE
TRKTAB.LENGTH: EQU $-TRKTAB
RECLENTAB: DS	2	;2 Drive record length table
MOTOR:	DEFS	1	;DRIVE MOTOR TURN-OFF TIMER
RECLEN:	DEFS	1	;Current disk 'Record Length'
TRACK:	DEFS	1
SECTOR:	DEFS	1
CMDTYP:	DEFS	1	;COMMAND BYTE FOR READS/WRITES
RETRY:	DEFS	1	;DISK OPERATION RE-TRY COUNT
IOPTR:	DEFS	2	;DISK I/O BUFFER POINTER
OLDPTR:	DS	2	;A scratch location used by RESTORE
RWFLAG:	DS	1	;Discriminates reads/writes
;
;
;
;	KEYBOARD DATA INPUT FIFO VARIABLES

FIFO:	DEFS	16	;CONSOLE INPUT FIFO
FIFCNT:	DEFS	1	;FIFO DATA COUNTER
FIFIN:	DEFS	1	;FIFI INPUT POINTER
FIFOUT:	DEFS	1	;FIFO OUTPUT POINTER
;
;
;	STACK POINTER SAVE AND LOCAL STACK FOR INTERRUPT ROUTINES
TMPSTK:	EQU	$	;Internal stack for all routines
	DEFS	16	;LOCAL STACK FOR INTERRUPTS
SPSAVE:	DS	2	;Save stack pointer
;
;
;	'SOFTWARE' VECTORS FOR INTERRUPT SERVICE ROUTINES

TIKVEC:	DEFS	2	;ONE SECOND INTERRUPT ROUTINE VECTOR
PINVEC:	DEFS	2	;PARALLEL CONSOLE INPUT VECTOR
SINVEC:	DEFS	2	;SERIAL CONSOLE INPUT VECTOR
;
;
;	CLOCK-TIMER INTERRUPT VARIABLES

TIKCNT:	DEFS	2	;BINARY CLOCK TICK COUNTER
DAY:	DEFS	1	;CALENDAR DAY
MONTH:	DEFS	1	;	  MONTH
YEAR:	DEFS	1	;	  YEAR
HRS:	DEFS	1	;CLOCK HOURS REGISTER
MINS:	DEFS	1	;      MINUTES RETISTER
SECS:	DEFS	1	;      SECONDS REGISTER
;
;	CRT OUTPUT DRIVER VARIABLES

CURSOR:	DEFS	2	;CURSOR POINTER
CHRSAV:	DEFS	1	;CHARACTER OVERLAYED BY CURSOR
CSRCHR:	DEFS	1	;CHARACTER USED FOR A CURSOR
BASE:	DEFS	1	;CURRENT CONTENTS OF SCROLL REGISTER
LEADIN:	DEFS	1	;STATE OF LEAD-IN SEQUENCE HANDLER
;
;
;	NULL PAD COUNT FOR SERIAL OUTPUT DELAY

;NULLS:	DEFS	1	;# OF NULLS SENT AFTER CONTROL CHARS.
;
;
;	CONSOLE MONITOR PROGRAM VARIABLES

PARAM1:	DEFS	2	;STORAGE FOR NUMBERS READ
PARAM2:	DEFS	2	; FROM LINE INPUT BUFFER
PARAM3:	DEFS	2	; BY 'PARAMS' SUBROUTINE
PARAM4:	DEFS	2
ESCFLG:	DEFS	1	;CONSOLE ESCAPE FLAG
COFLAG:	DEFS	1	;CONSOLE OUTPUT TOGGLE
LAST:	DEFS	2	;LAST ADDRESS USED BY 'MEMDMP'
HLSAV:	DS	2	;Storage of values at entry
DESAV:	DS	2
BCSAV:	DS	2
SPSAV:	DS	2
ROUTINE.ADDRESS: DS 2	;Where to restart on errors
OFFSET:	DS	1	;the offset (function #)
LINBUF:	DEFS	64	;CONSOLE LINE INPUT BUFFER
;
STACK:	EQU	0FFFFH	;Top of memory
;STACKLENGTH: EQU STACK-[LINBUF+64] !Ignore error
;
	END
